1 00:00:04,069 --> 00:00:07,230 So a few videos ago we changed our 2 00:00:07,230 --> 00:00:09,929 content providers query function to use 3 00:00:09,929 --> 00:00:12,509 appendWhereEscapeString rather than 4 00:00:12,509 --> 00:00:14,940 appendWhere. Now I didn't go into a lot 5 00:00:14,940 --> 00:00:17,100 of detail about this function because we 6 00:00:17,100 --> 00:00:19,109 couldn't test the code at that stage, but 7 00:00:19,109 --> 00:00:21,689 we finished the previous video by seeing 8 00:00:21,689 --> 00:00:24,029 that appendWhere worked, but there was a 9 00:00:24,029 --> 00:00:26,189 problem using appendWhereEscapeString. 10 00:00:26,189 --> 00:00:27,630 Obviously it's not quite as 11 00:00:27,630 --> 00:00:29,130 straightforward as just swapping one 12 00:00:29,130 --> 00:00:30,990 function out for the other. So there's 13 00:00:30,990 --> 00:00:32,579 something going on with the escape 14 00:00:32,579 --> 00:00:34,739 String version of appendWhere, and it 15 00:00:34,739 --> 00:00:36,780 would be really useful to log the SQL 16 00:00:36,780 --> 00:00:38,850 that it produces to see what's going on. 17 00:00:38,850 --> 00:00:41,760 Unfortunately though, we're using a query 18 00:00:41,760 --> 00:00:44,100 Builder, and the query builder class does 19 00:00:44,100 --> 00:00:46,320 not provide a function for accessing the 20 00:00:46,320 --> 00:00:49,170 SQL that it creates. Now when you find 21 00:00:49,170 --> 00:00:51,390 yourself in a situation like this, it's 22 00:00:51,390 --> 00:00:53,309 worth having a look at the source code 23 00:00:53,309 --> 00:00:55,800 for the classes that you're using. So in 24 00:00:55,800 --> 00:00:57,360 this case we're interested in the query 25 00:00:57,360 --> 00:01:00,480 Builder source code, which we can get by 26 00:01:00,480 --> 00:01:02,699 control clicking on the query function 27 00:01:02,699 --> 00:01:04,530 called in our code - so right down at the 28 00:01:04,530 --> 00:01:06,900 end of the query function that we've 29 00:01:06,900 --> 00:01:10,110 added to this AppProvider. So I'm going to 30 00:01:10,110 --> 00:01:11,580 ctrl click that, or command if you're on 31 00:01:11,580 --> 00:01:14,520 a Mac. You can see here now that we've 32 00:01:14,520 --> 00:01:16,259 got to the source of the query function 33 00:01:16,259 --> 00:01:17,939 that we're calling, and it doesn't really 34 00:01:17,939 --> 00:01:20,310 do much. Now as this is a Kotlin course, 35 00:01:20,310 --> 00:01:23,009 I'll very briefly talk about overloading 36 00:01:23,009 --> 00:01:24,869 functions in Java. Now that's not 37 00:01:24,869 --> 00:01:27,150 something we do in Kotlin, because Kotlin 38 00:01:27,150 --> 00:01:29,100 provides default values for function 39 00:01:29,100 --> 00:01:31,680 arguments. Java, on the other hand, doesn't 40 00:01:31,680 --> 00:01:34,049 let you do that. Instead Java lets you 41 00:01:34,049 --> 00:01:35,939 create different versions of the same 42 00:01:35,939 --> 00:01:38,490 function or method, as it's Java with 43 00:01:38,490 --> 00:01:40,380 different arguments. That's called 44 00:01:40,380 --> 00:01:42,720 overloading the method. Now the query 45 00:01:42,720 --> 00:01:44,850 method we're using is an overloaded 46 00:01:44,850 --> 00:01:47,520 version of another query method. Now this 47 00:01:47,520 --> 00:01:49,770 one doesn't do much - it just calls the 48 00:01:49,770 --> 00:01:52,020 one it's overloading and returns the 49 00:01:52,020 --> 00:01:54,210 result of calling that. Now if we 50 00:01:54,210 --> 00:01:55,950 actually can click on the query function 51 00:01:55,950 --> 00:01:59,670 that it's calling - this one here - we can 52 00:01:59,670 --> 00:02:00,990 see the source for that one on-screen 53 00:02:00,990 --> 00:02:03,030 now. Now this version of the query is 54 00:02:03,030 --> 00:02:04,890 slightly different. It's got an extra 55 00:02:04,890 --> 00:02:07,290 cancellation signal argument right at 56 00:02:07,290 --> 00:02:09,060 the end of the parameter list. Now the 57 00:02:09,060 --> 00:02:11,068 function we call just sets that argument 58 00:02:11,068 --> 00:02:13,980 to null. In Kotlin we'd use a default 59 00:02:13,980 --> 00:02:16,200 value of null, rather than overloading 60 00:02:16,200 --> 00:02:18,840 the function. In Java however, you declare 61 00:02:18,840 --> 00:02:20,909 the method again with the additional 62 00:02:20,909 --> 00:02:22,590 parameter. Now that's a bit more 63 00:02:22,590 --> 00:02:24,480 verbose, but that's all that's meant by 64 00:02:24,480 --> 00:02:27,480 method overloading in Java. so watch out 65 00:02:27,480 --> 00:02:28,860 for this when you're reading the Java 66 00:02:28,860 --> 00:02:30,930 source code. There'll often be different 67 00:02:30,930 --> 00:02:33,180 versions of the same method, and often 68 00:02:33,180 --> 00:02:34,620 the one you will be using will call 69 00:02:34,620 --> 00:02:36,239 another version with different 70 00:02:36,239 --> 00:02:38,400 parameters. Alright so let's dig 71 00:02:38,400 --> 00:02:39,959 in and see what this particular method's 72 00:02:39,959 --> 00:02:42,390 doing. Now when I'm reading the Android 73 00:02:42,390 --> 00:02:44,340 sources, I don't try to understand 74 00:02:44,340 --> 00:02:47,190 everything in fine detail, at least not 75 00:02:47,190 --> 00:02:47,910 at first. 76 00:02:47,910 --> 00:02:49,620 Now and then you'll hit a problem that's 77 00:02:49,620 --> 00:02:51,480 complex enough that you have to check 78 00:02:51,480 --> 00:02:54,030 the source very carefully. Often though, a 79 00:02:54,030 --> 00:02:55,950 quick scan of the methods and comments 80 00:02:55,950 --> 00:02:58,319 is enough. Now this particular method's 81 00:02:58,319 --> 00:03:00,450 quite straightforward. Firstly, if no 82 00:03:00,450 --> 00:03:02,910 tables were specified it returns null, 83 00:03:02,910 --> 00:03:04,500 right at the start of the method there - 84 00:03:04,500 --> 00:03:07,620 you can see line 376. That makes sense. 85 00:03:07,620 --> 00:03:08,940 We're not going to get anything back if 86 00:03:08,940 --> 00:03:10,650 we haven't specified any tables to query. 87 00:03:10,650 --> 00:03:13,709 The next bit of code down here, starting 88 00:03:13,709 --> 00:03:16,859 on line 379, deals with the escaping to 89 00:03:16,859 --> 00:03:19,290 prevent injection attacks. So you can see 90 00:03:19,290 --> 00:03:21,299 that it's calling a validateSql 91 00:03:21,299 --> 00:03:24,870 method there on line 389, and it's 92 00:03:24,870 --> 00:03:26,430 calling that method of the SQLite 93 00:03:26,430 --> 00:03:28,500 Database class. And that method will 94 00:03:28,500 --> 00:03:30,239 throw an exception if anything dodgy is 95 00:03:30,239 --> 00:03:33,060 detected. The comment gives that away - the 96 00:03:33,060 --> 00:03:35,069 end of the line there, "will throw if query is 97 00:03:35,069 --> 00:03:37,769 invalid". So far so good, but what happens 98 00:03:37,769 --> 00:03:39,829 next? Well it builds up a query string. 99 00:03:39,829 --> 00:03:41,940 You can see it's starting to do that on 100 00:03:41,940 --> 00:03:44,549 line 392, and it's building up that query 101 00:03:44,549 --> 00:03:45,870 string from the arguments that were 102 00:03:45,870 --> 00:03:48,090 passed in. Now that string's very similar 103 00:03:48,090 --> 00:03:50,819 to the sequel that it validated. So read 104 00:03:50,819 --> 00:03:52,440 the comment to understand why it builds 105 00:03:52,440 --> 00:03:54,810 the query string twice. So as it says up 106 00:03:54,810 --> 00:03:55,500 here, 107 00:03:55,500 --> 00:03:59,519 "An attacker can't create an expression 108 00:03:59,519 --> 00:04:01,470 that would escape the SQL expression 109 00:04:01,470 --> 00:04:04,349 while maintaining balance parentheses in 110 00:04:04,349 --> 00:04:07,590 both the wrapped and original forms." So 111 00:04:07,590 --> 00:04:09,389 basically, if an injection attack is 112 00:04:09,389 --> 00:04:12,389 tried, this code will either throw an 113 00:04:12,389 --> 00:04:14,459 exception when validating the SQL 114 00:04:14,459 --> 00:04:17,760 string, or it'll attempt to execute an invalid 115 00:04:17,760 --> 00:04:20,639 string that the database will reject. So 116 00:04:20,639 --> 00:04:22,140 it's not possible for both the escaped 117 00:04:22,140 --> 00:04:24,419 and original sequel to be valid in the 118 00:04:24,419 --> 00:04:26,610 case of an attempted attack. So the 119 00:04:26,610 --> 00:04:27,880 result is that an 120 00:04:27,880 --> 00:04:30,160 attack string will either cause an exception 121 00:04:30,160 --> 00:04:31,930 to be thrown by the validate single 122 00:04:31,930 --> 00:04:34,390 method, or it'll cause an error when the 123 00:04:34,390 --> 00:04:36,520 database tried to execute the SQL. 124 00:04:36,520 --> 00:04:38,560 Whichever happens though, the attack 125 00:04:38,560 --> 00:04:40,810 won't work. But with that said, we're 126 00:04:40,810 --> 00:04:42,550 not here to understand the fine details 127 00:04:42,550 --> 00:04:45,220 of how escaping works. We're trying to 128 00:04:45,220 --> 00:04:46,600 see if there's anything that can help us 129 00:04:46,600 --> 00:04:49,930 to work out why our query failed. So 130 00:04:49,930 --> 00:04:51,100 scrolling down a little bit, 131 00:04:51,100 --> 00:04:53,920 notice the code on line 396. That's 132 00:04:53,920 --> 00:04:56,350 pretty interesting. After building the 133 00:04:56,350 --> 00:04:58,360 original SQL string in the variable 134 00:04:58,360 --> 00:05:01,540 SQL, that method's actually logging it. Now 135 00:05:01,540 --> 00:05:03,190 it does things slightly different to how 136 00:05:03,190 --> 00:05:05,260 we've been logging. Now we use debug 137 00:05:05,260 --> 00:05:07,300 level logging, and rely on those log 138 00:05:07,300 --> 00:05:09,490 calls being stripped out when we build 139 00:05:09,490 --> 00:05:12,040 the release version of the app. But in a 140 00:05:12,040 --> 00:05:14,350 library class like this one, stripping 141 00:05:14,350 --> 00:05:15,970 out the debug logging calls would make 142 00:05:15,970 --> 00:05:18,280 life very hard for people using the 143 00:05:18,280 --> 00:05:20,890 class - us in other words. So to prevent 144 00:05:20,890 --> 00:05:22,840 all those debug log entries from filling 145 00:05:22,840 --> 00:05:24,910 up the logcat and slowing the app down, 146 00:05:24,910 --> 00:05:27,370 this code checks if debug logging is 147 00:05:27,370 --> 00:05:31,180 enabled and only calls Log.d if it is. So 148 00:05:31,180 --> 00:05:33,640 at this point, yay we've struck gold. We 149 00:05:33,640 --> 00:05:35,680 want to log the SQL and this method 150 00:05:35,680 --> 00:05:37,900 is already doing it for us. So all we 151 00:05:37,900 --> 00:05:39,400 have to do is work out how to tell 152 00:05:39,400 --> 00:05:42,250 Android to enable debug logging for this 153 00:05:42,250 --> 00:05:44,500 particular class. As it turns out it's 154 00:05:44,500 --> 00:05:47,350 very easy to do, so let's do that. Now 155 00:05:47,350 --> 00:05:49,000 what we'll need first is the tag that 156 00:05:49,000 --> 00:05:51,040 the class is using, so I'm going to come 157 00:05:51,040 --> 00:05:54,160 over here and click on the tag, and I'm 158 00:05:54,160 --> 00:05:56,020 going to double click on the contents of 159 00:05:56,020 --> 00:05:58,150 that tag. In this case it's set to SQL 160 00:05:58,150 --> 00:05:59,710 iteQueryBuilder. I'm going to take a copy 161 00:05:59,710 --> 00:06:01,240 of that, making sure that I didn't 162 00:06:01,240 --> 00:06:02,500 include the double quotes when I've 163 00:06:02,500 --> 00:06:04,230 copied it. We just want the tags value, 164 00:06:04,230 --> 00:06:07,360 not a delimited Java string. Alright so 165 00:06:07,360 --> 00:06:09,790 to enable logging at the debug level for 166 00:06:09,790 --> 00:06:12,220 this tag, we can use adb to set a 167 00:06:12,220 --> 00:06:14,590 property on the device or emulator. So 168 00:06:14,590 --> 00:06:17,170 I'm going to open the terminal window, and I'm 169 00:06:17,170 --> 00:06:24,220 going to type adb shell setprop log dot 170 00:06:24,220 --> 00:06:27,250 tag dot. Then I'm going to paste in what I 171 00:06:27,250 --> 00:06:28,780 copied from the Java source code, 172 00:06:28,780 --> 00:06:31,090 in this case eSQLiteQueryBuilder space 173 00:06:31,090 --> 00:06:36,040 then DEBUG in uppercase, press enter. Now 174 00:06:36,040 --> 00:06:38,470 the properties name is log dot tag dot, and 175 00:06:38,470 --> 00:06:39,760 then the tag that we're interested in as 176 00:06:39,760 --> 00:06:41,380 you saw there, which I've pasted into the 177 00:06:41,380 --> 00:06:41,650 terminal. 178 00:06:41,650 --> 00:06:44,530 Now the class is logging the SQL at 179 00:06:44,530 --> 00:06:46,419 the debug level, so that's why I've used 180 00:06:46,419 --> 00:06:49,300 debug as the property value. And it's 181 00:06:49,300 --> 00:06:51,669 actually as easy as that. If a class in the 182 00:06:51,669 --> 00:06:53,919 Android framework does some logging, you 183 00:06:53,919 --> 00:06:55,840 can enable it by setting the property 184 00:06:55,840 --> 00:06:57,940 using adb shell. Now there was another 185 00:06:57,940 --> 00:07:00,460 way to do that by creating a prop file 186 00:07:00,460 --> 00:07:02,889 on the emulator, but as many students 187 00:07:02,889 --> 00:07:04,479 are using their phones to run these apps, 188 00:07:04,479 --> 00:07:06,729 I prefer not making a change to the 189 00:07:06,729 --> 00:07:08,949 phone itself. So this method that we've 190 00:07:08,949 --> 00:07:11,050 done here doesn't make any permanent 191 00:07:11,050 --> 00:07:13,270 change, but what it does mean is that you 192 00:07:13,270 --> 00:07:15,490 have to enter the command again every 193 00:07:15,490 --> 00:07:17,169 time the device - the emulator - is 194 00:07:17,169 --> 00:07:19,360 restarted. Now if you are using a 195 00:07:19,360 --> 00:07:21,669 physical device for this course, reboot 196 00:07:21,669 --> 00:07:23,740 it after this video to put the logging 197 00:07:23,740 --> 00:07:25,750 back to how it was. Alright, so with all 198 00:07:25,750 --> 00:07:27,699 that said, does it work? Well at the end 199 00:07:27,699 --> 00:07:29,229 of the previous video we changed the 200 00:07:29,229 --> 00:07:31,449 code so that it worked. So I'm going to 201 00:07:31,449 --> 00:07:33,849 exit out of terminal now, and we'll 202 00:07:33,849 --> 00:07:34,870 go back. We'll close down some of 203 00:07:34,870 --> 00:07:35,979 these files now, we don't need 204 00:07:35,979 --> 00:07:38,130 them anymore, or just that one file. So 205 00:07:38,130 --> 00:07:40,270 looking at the code, we changed it back 206 00:07:40,270 --> 00:07:42,940 to appendWhere on line 80, as you can see 207 00:07:42,940 --> 00:07:44,860 there. So it's using that rather than 208 00:07:44,860 --> 00:07:46,990 appendWhereEscapeString. So let's run the 209 00:07:46,990 --> 00:07:49,120 app first and see what the valid SQL 210 00:07:49,120 --> 00:07:50,199 looks like, because we know that that 211 00:07:50,199 --> 00:07:57,630 that's working. 212 00:07:57,630 --> 00:07:59,940 Alright so there's the debug coming from 213 00:07:59,940 --> 00:08:01,770 SQLiteQueryBuilder, as you can see 214 00:08:01,770 --> 00:08:03,960 there. So that obviously worked - the 215 00:08:03,960 --> 00:08:05,940 command, the adb shell setprop command 216 00:08:05,940 --> 00:08:07,920 that we used - and you can see it's using 217 00:08:07,920 --> 00:08:09,690 a SELECT Name comma SortOrder FROM 218 00:08:09,690 --> 00:08:12,270 Tasks WHERE, then parentheses underscore ID 219 00:08:12,270 --> 00:08:14,970 equals 2 ORDER BY SortOrder. No surprises 220 00:08:14,970 --> 00:08:16,530 there, and what I could have done to 221 00:08:16,530 --> 00:08:18,060 get, to find that quickly, I could have 222 00:08:18,060 --> 00:08:20,490 just done a filter on 223 00:08:20,490 --> 00:08:23,970 logcat, sqlite query builder or just do an 224 00:08:23,970 --> 00:08:25,470 sqlite to find that really 225 00:08:25,470 --> 00:08:27,090 quickly. If you're having trouble that's one 226 00:08:27,090 --> 00:08:29,040 way to quickly find it, but again, no 227 00:08:29,040 --> 00:08:31,260 surprise there. That's valid SQL and 228 00:08:31,260 --> 00:08:33,049 of course the app was actually working. 229 00:08:33,049 --> 00:08:35,370 Alright, so let's now change the code 230 00:08:35,370 --> 00:08:38,669 back to use the appendWhereEscape 231 00:08:38,669 --> 00:08:41,070 String and see what went wrong. So what 232 00:08:41,070 --> 00:08:42,270 I'm going to do is duplicate that line 233 00:08:42,270 --> 00:08:45,120 initially, then I'm going to comment that 234 00:08:45,120 --> 00:08:47,940 out. Then I'm going to set the second line to 235 00:08:47,940 --> 00:08:51,240 EscapeString. So let's stop the app and 236 00:08:51,240 --> 00:08:51,930 start it again. 237 00:08:51,930 --> 00:08:54,510 Run it, then we'll swing over to the 238 00:08:54,510 --> 00:08:57,810 logcat and see what it's done. Actually I'll open 239 00:08:57,810 --> 00:09:00,030 the logcat again. You can see now that's 240 00:09:00,030 --> 00:09:02,040 the cause of our problem. Looking at the 241 00:09:02,040 --> 00:09:04,620 WHERE clause we've got parenthesis id, 242 00:09:04,620 --> 00:09:07,250 underscore id equals two in single quotes. 243 00:09:07,250 --> 00:09:09,750 We only want the value 2 here to be 244 00:09:09,750 --> 00:09:11,220 escaped. We don't want the entire 245 00:09:11,220 --> 00:09:13,620 conditioning quotes as showing at the 246 00:09:13,620 --> 00:09:15,660 moment, so that's the reason why we're 247 00:09:15,660 --> 00:09:18,060 not getting any data back. So to fix this 248 00:09:18,060 --> 00:09:20,820 we can use appendWhere for the column 249 00:09:20,820 --> 00:09:23,400 name, then appendWhereEscapeString for 250 00:09:23,400 --> 00:09:26,010 the value - in this case our taskId. So let's 251 00:09:26,010 --> 00:09:27,810 have a go at doing that. So I'm going to 252 00:09:27,810 --> 00:09:30,450 uncomment this again, the appendWhere. I'm 253 00:09:30,450 --> 00:09:31,860 going to change that now so it's just 254 00:09:31,860 --> 00:09:34,800 got, in double quotes, dollar left and 255 00:09:34,800 --> 00:09:37,020 right curly braces TaskContract dot 256 00:09:37,020 --> 00:09:39,510 Columns.ID, then right 257 00:09:39,510 --> 00:09:41,580 curly brace equals. I'm going to delete 258 00:09:41,580 --> 00:09:44,520 that last part there. So we've just got 259 00:09:44,520 --> 00:09:47,130 the equals and a space, and then let's 260 00:09:47,130 --> 00:09:48,990 fix the next line that's already 261 00:09:48,990 --> 00:09:51,120 got the appendWhereEscapeString, and I'm 262 00:09:51,120 --> 00:09:53,910 going to delete everything other than 263 00:09:53,910 --> 00:09:56,850 the dollar taskId. So you can see that 264 00:09:56,850 --> 00:09:59,160 we're just escaping now, effectively, the 265 00:09:59,160 --> 00:10:01,290 taskId. Everything else is just a 266 00:10:01,290 --> 00:10:03,660 general appendWhere on the previous 267 00:10:03,660 --> 00:10:05,190 line. So now that we've done that, 268 00:10:05,190 --> 00:10:10,620 let's stop this and start it again, swing 269 00:10:10,620 --> 00:10:12,110 over to our logcat. 270 00:10:12,110 --> 00:10:14,370 And you can see now we've got some valid 271 00:10:14,370 --> 00:10:16,920 SQL: SELECT Name comma SortOrder FROM 272 00:10:16,920 --> 00:10:18,870 Tasks WHERE, and in parentheses underscore 273 00:10:18,870 --> 00:10:21,000 id equals, then the value here in single 274 00:10:21,000 --> 00:10:23,610 quotes 2. So that's now correct, and if I 275 00:10:23,610 --> 00:10:26,250 actually delete this filter now, we've 276 00:10:26,250 --> 00:10:29,490 got rows in returned cursor equals 1 and 277 00:10:29,490 --> 00:10:30,930 we've actually got our records showing 278 00:10:30,930 --> 00:10:32,640 on the screen there. Now we added the 279 00:10:32,640 --> 00:10:34,350 appendWhereEscapeString to the code a 280 00:10:34,350 --> 00:10:36,840 few videos ago, but trying to explain all 281 00:10:36,840 --> 00:10:37,920 this when we couldn't run the app would 282 00:10:37,920 --> 00:10:39,990 have been confusing. Seeing the different 283 00:10:39,990 --> 00:10:41,520 SQL that it produces makes it much 284 00:10:41,520 --> 00:10:44,730 easier to see how to use it correctly. So 285 00:10:44,730 --> 00:10:46,980 you use appendWhereEscapeString to 286 00:10:46,980 --> 00:10:49,170 append the values, not to append an 287 00:10:49,170 --> 00:10:50,640 entire WHERE clause. 288 00:10:50,640 --> 00:10:52,290 Alright, so now that we've got that fixed 289 00:10:52,290 --> 00:10:55,440 I'll just close down logcat, and let's 290 00:10:55,440 --> 00:10:57,210 make the same changes to the commented 291 00:10:57,210 --> 00:10:59,310 out code for the TIMINGS and TASK 292 00:10:59,310 --> 00:11:01,470 DURATIONS, because if we don't do it now 293 00:11:01,470 --> 00:11:02,910 it'll be too easy to forget to do it 294 00:11:02,910 --> 00:11:04,530 later. So what I'm going to do is take a 295 00:11:04,530 --> 00:11:07,140 copy of that first line, change that to 296 00:11:07,140 --> 00:11:10,110 just the appendWhere. I'm going to 297 00:11:10,110 --> 00:11:12,240 delete after the equal signs, equal sign, 298 00:11:12,240 --> 00:11:14,730 up and to the double quote. I'll put a 299 00:11:14,730 --> 00:11:17,160 space after there. Then the second line, 300 00:11:17,160 --> 00:11:19,710 I'm going to delete everything but, in 301 00:11:19,710 --> 00:11:21,660 the double quotes rather, everything but 302 00:11:21,660 --> 00:11:23,730 the $timingId, and we'll do the 303 00:11:23,730 --> 00:11:27,120 same for the durations, TASK_DURATIONS_ 304 00:11:27,120 --> 00:11:31,100 ID. Change the first call to appendWhere, 305 00:11:31,100 --> 00:11:32,760 in double quotes, 306 00:11:32,760 --> 00:11:36,420 delete after the equals sign. Then leave 307 00:11:36,420 --> 00:11:38,100 the appendWhereEscapeString as the 308 00:11:38,100 --> 00:11:40,170 second call, deleting everything but in 309 00:11:40,170 --> 00:11:43,260 this case, the $durationId. So that 310 00:11:43,260 --> 00:11:45,150 should hopefully work when it comes time 311 00:11:45,150 --> 00:11:47,310 to use that code. Now with that said, it's 312 00:11:47,310 --> 00:11:49,500 quite easy to make a mistake when typing 313 00:11:49,500 --> 00:11:51,660 code into a comment, obviously because 314 00:11:51,660 --> 00:11:53,820 there's no syntax checking. But I'd much 315 00:11:53,820 --> 00:11:55,590 rather have a minor mistake in there and 316 00:11:55,590 --> 00:11:57,570 have to correct it when we uncomment the 317 00:11:57,570 --> 00:11:59,280 code, rather than forgetting to use 318 00:11:59,280 --> 00:12:02,130 EscapeString correctly. Alright, so at 319 00:12:02,130 --> 00:12:03,930 this point queries against our database 320 00:12:03,930 --> 00:12:06,780 using the AppProvider class are working 321 00:12:06,780 --> 00:12:08,730 now. In the next video we'll finish off 322 00:12:08,730 --> 00:12:10,950 the Content Provider, writing the code to 323 00:12:10,950 --> 00:12:13,320 insert new rows, as well as updating rows 324 00:12:13,320 --> 00:12:16,140 and deleting existing ones. So I'll see 325 00:12:16,140 --> 00:12:19,010 you in the next video.