fmdb.m 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455
  1. #import <Foundation/Foundation.h>
  2. #import "FMDatabase.h"
  3. #import "FMDatabaseAdditions.h"
  4. #import "FMDatabasePool.h"
  5. #import "FMDatabaseQueue.h"
  6. #define FMDBQuickCheck(SomeBool) { if (!(SomeBool)) { NSLog(@"Failure on line %d", __LINE__); abort(); } }
  7. void testPool(NSString *dbPath);
  8. void testDateFormat();
  9. void FMDBReportABugFunction();
  10. int main (int argc, const char * argv[]) {
  11. @autoreleasepool {
  12. FMDBReportABugFunction();
  13. NSString *dbPath = @"/tmp/tmp.db";
  14. // delete the old db.
  15. NSFileManager *fileManager = [NSFileManager defaultManager];
  16. [fileManager removeItemAtPath:dbPath error:nil];
  17. FMDatabase *db = [FMDatabase databaseWithPath:dbPath];
  18. NSLog(@"Is SQLite compiled with it's thread safe options turned on? %@!", [FMDatabase isSQLiteThreadSafe] ? @"Yes" : @"No");
  19. {
  20. // -------------------------------------------------------------------------------
  21. // Un-opened database check.
  22. FMDBQuickCheck([db executeQuery:@"select * from table"] == nil);
  23. NSLog(@"%d: %@", [db lastErrorCode], [db lastErrorMessage]);
  24. }
  25. if (![db open]) {
  26. NSLog(@"Could not open db.");
  27. return 0;
  28. }
  29. // kind of experimentalish.
  30. [db setShouldCacheStatements:YES];
  31. // create a bad statement, just to test the error code.
  32. [db executeUpdate:@"blah blah blah"];
  33. FMDBQuickCheck([db hadError]);
  34. if ([db hadError]) {
  35. NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
  36. }
  37. NSError *err = 0x00;
  38. FMDBQuickCheck(![db update:@"blah blah blah" withErrorAndBindings:&err]);
  39. FMDBQuickCheck(err != nil);
  40. FMDBQuickCheck([err code] == SQLITE_ERROR);
  41. NSLog(@"err: '%@'", err);
  42. // empty strings should still return a value.
  43. FMDBQuickCheck(([db boolForQuery:@"SELECT ? not null", @""]));
  44. // same with empty bits o' mutable data
  45. FMDBQuickCheck(([db boolForQuery:@"SELECT ? not null", [NSMutableData data]]));
  46. // same with empty bits o' data
  47. FMDBQuickCheck(([db boolForQuery:@"SELECT ? not null", [NSData data]]));
  48. // how do we do pragmas? Like so:
  49. FMResultSet *ps = [db executeQuery:@"PRAGMA journal_mode=delete"];
  50. FMDBQuickCheck(![db hadError]);
  51. FMDBQuickCheck(ps);
  52. FMDBQuickCheck([ps next]);
  53. [ps close];
  54. // oh, but some pragmas require updates?
  55. [db executeUpdate:@"PRAGMA page_size=2048"];
  56. FMDBQuickCheck(![db hadError]);
  57. // what about a vacuum?
  58. [db executeUpdate:@"vacuum"];
  59. FMDBQuickCheck(![db hadError]);
  60. // but of course, I don't bother checking the error codes below.
  61. // Bad programmer, no cookie.
  62. [db executeUpdate:@"create table test (a text, b text, c integer, d double, e double)"];
  63. [db beginTransaction];
  64. int i = 0;
  65. while (i++ < 20) {
  66. [db executeUpdate:@"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,
  67. @"hi'", // look! I put in a ', and I'm not escaping it!
  68. [NSString stringWithFormat:@"number %d", i],
  69. [NSNumber numberWithInt:i],
  70. [NSDate date],
  71. [NSNumber numberWithFloat:2.2f]];
  72. }
  73. [db commit];
  74. // do it again, just because
  75. [db beginTransaction];
  76. i = 0;
  77. while (i++ < 20) {
  78. [db executeUpdate:@"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,
  79. @"hi again'", // look! I put in a ', and I'm not escaping it!
  80. [NSString stringWithFormat:@"number %d", i],
  81. [NSNumber numberWithInt:i],
  82. [NSDate date],
  83. [NSNumber numberWithFloat:2.2f]];
  84. }
  85. [db commit];
  86. FMResultSet *rs = [db executeQuery:@"select rowid,* from test where a = ?", @"hi'"];
  87. while ([rs next]) {
  88. // just print out what we've got in a number of formats.
  89. NSLog(@"%d %@ %@ %@ %@ %f %f",
  90. [rs intForColumn:@"c"],
  91. [rs stringForColumn:@"b"],
  92. [rs stringForColumn:@"a"],
  93. [rs stringForColumn:@"rowid"],
  94. [rs dateForColumn:@"d"],
  95. [rs doubleForColumn:@"d"],
  96. [rs doubleForColumn:@"e"]);
  97. if (!([[rs columnNameForIndex:0] isEqualToString:@"rowid"] &&
  98. [[rs columnNameForIndex:1] isEqualToString:@"a"])
  99. ) {
  100. NSLog(@"WHOA THERE BUDDY, columnNameForIndex ISN'T WORKING!");
  101. return 7;
  102. }
  103. }
  104. // close the result set.
  105. // it'll also close when it's dealloc'd, but we're closing the database before
  106. // the autorelease pool closes, so sqlite will complain about it.
  107. [rs close];
  108. FMDBQuickCheck(![db hasOpenResultSets]);
  109. rs = [db executeQuery:@"select rowid, a, b, c from test"];
  110. while ([rs next]) {
  111. FMDBQuickCheck([rs[0] isEqual:rs[@"rowid"]]);
  112. FMDBQuickCheck([rs[1] isEqual:rs[@"a"]]);
  113. FMDBQuickCheck([rs[2] isEqual:rs[@"b"]]);
  114. FMDBQuickCheck([rs[3] isEqual:rs[@"c"]]);
  115. }
  116. [rs close];
  117. [db executeUpdate:@"create table ull (a integer)"];
  118. [db executeUpdate:@"insert into ull (a) values (?)" , [NSNumber numberWithUnsignedLongLong:ULLONG_MAX]];
  119. rs = [db executeQuery:@"select a from ull"];
  120. while ([rs next]) {
  121. unsigned long long a = [rs unsignedLongLongIntForColumnIndex:0];
  122. unsigned long long b = [rs unsignedLongLongIntForColumn:@"a"];
  123. FMDBQuickCheck(a == ULLONG_MAX);
  124. FMDBQuickCheck(b == ULLONG_MAX);
  125. }
  126. // check case sensitive result dictionary.
  127. [db executeUpdate:@"create table cs (aRowName integer, bRowName text)"];
  128. FMDBQuickCheck(![db hadError]);
  129. [db executeUpdate:@"insert into cs (aRowName, bRowName) values (?, ?)" , [NSNumber numberWithBool:1], @"hello"];
  130. FMDBQuickCheck(![db hadError]);
  131. rs = [db executeQuery:@"select * from cs"];
  132. while ([rs next]) {
  133. NSDictionary *d = [rs resultDictionary];
  134. FMDBQuickCheck([d objectForKey:@"aRowName"]);
  135. FMDBQuickCheck(![d objectForKey:@"arowname"]);
  136. FMDBQuickCheck([d objectForKey:@"bRowName"]);
  137. FMDBQuickCheck(![d objectForKey:@"browname"]);
  138. }
  139. // check funky table names + getTableSchema
  140. [db executeUpdate:@"create table '234 fds' (foo text)"];
  141. FMDBQuickCheck(![db hadError]);
  142. rs = [db getTableSchema:@"234 fds"];
  143. FMDBQuickCheck([rs next]);
  144. [rs close];
  145. #if SQLITE_VERSION_NUMBER >= 3007017
  146. {
  147. uint32_t appID = NSHFSTypeCodeFromFileType(NSFileTypeForHFSTypeCode('fmdb'));
  148. [db setApplicationID:appID];
  149. uint32_t rAppID = [db applicationID];
  150. NSLog(@"rAppID: %d", rAppID);
  151. FMDBQuickCheck(rAppID == appID);
  152. [db setApplicationIDString:@"acrn"];
  153. NSString *s = [db applicationIDString];
  154. NSLog(@"s: '%@'", s);
  155. FMDBQuickCheck([s isEqualToString:@"acrn"]);
  156. }
  157. #endif
  158. {
  159. // -------------------------------------------------------------------------------
  160. // Named parameters count test.
  161. FMDBQuickCheck([db executeUpdate:@"create table namedparamcounttest (a text, b text, c integer, d double)"]);
  162. NSMutableDictionary *dictionaryArgs = [NSMutableDictionary dictionary];
  163. [dictionaryArgs setObject:@"Text1" forKey:@"a"];
  164. [dictionaryArgs setObject:@"Text2" forKey:@"b"];
  165. [dictionaryArgs setObject:[NSNumber numberWithInt:1] forKey:@"c"];
  166. [dictionaryArgs setObject:[NSNumber numberWithDouble:2.0] forKey:@"d"];
  167. FMDBQuickCheck([db executeUpdate:@"insert into namedparamcounttest values (:a, :b, :c, :d)" withParameterDictionary:dictionaryArgs]);
  168. rs = [db executeQuery:@"select * from namedparamcounttest"];
  169. FMDBQuickCheck((rs != nil));
  170. [rs next];
  171. FMDBQuickCheck([[rs stringForColumn:@"a"] isEqualToString:@"Text1"]);
  172. FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"Text2"]);
  173. FMDBQuickCheck([rs intForColumn:@"c"] == 1);
  174. FMDBQuickCheck([rs doubleForColumn:@"d"] == 2.0);
  175. [rs close];
  176. // note that at this point, dictionaryArgs has way more values than we need, but the query should still work since
  177. // a is in there, and that's all we need.
  178. rs = [db executeQuery:@"select * from namedparamcounttest where a = :a" withParameterDictionary:dictionaryArgs];
  179. FMDBQuickCheck((rs != nil));
  180. FMDBQuickCheck([rs next]);
  181. [rs close];
  182. // ***** Please note the following codes *****
  183. dictionaryArgs = [NSMutableDictionary dictionary];
  184. [dictionaryArgs setObject:@"NewText1" forKey:@"a"];
  185. [dictionaryArgs setObject:@"NewText2" forKey:@"b"];
  186. [dictionaryArgs setObject:@"OneMoreText" forKey:@"OneMore"];
  187. BOOL rc = [db executeUpdate:@"update namedparamcounttest set a = :a, b = :b where b = 'Text2'" withParameterDictionary:dictionaryArgs];
  188. FMDBQuickCheck(rc);
  189. if (!rc) {
  190. NSLog(@"ERROR: %d - %@", db.lastErrorCode, db.lastErrorMessage);
  191. }
  192. }
  193. // ----------------------------------------------------------------------------------------
  194. // blob support.
  195. [db executeUpdate:@"create table blobTable (a text, b blob)"];
  196. // let's read in an image from safari's app bundle.
  197. NSData *safariCompass = [NSData dataWithContentsOfFile:@"/Applications/Safari.app/Contents/Resources/compass.icns"];
  198. if (safariCompass) {
  199. [db executeUpdate:@"insert into blobTable (a, b) values (?,?)", @"safari's compass", safariCompass];
  200. rs = [db executeQuery:@"select b from blobTable where a = ?", @"safari's compass"];
  201. if ([rs next]) {
  202. safariCompass = [rs dataForColumn:@"b"];
  203. [safariCompass writeToFile:@"/tmp/compass.icns" atomically:NO];
  204. // let's look at our fancy image that we just wrote out..
  205. system("/usr/bin/open /tmp/compass.icns");
  206. // ye shall read the header for this function, or suffer the consequences.
  207. safariCompass = [rs dataNoCopyForColumn:@"b"];
  208. [safariCompass writeToFile:@"/tmp/compass_data_no_copy.icns" atomically:NO];
  209. system("/usr/bin/open /tmp/compass_data_no_copy.icns");
  210. }
  211. else {
  212. NSLog(@"Could not select image.");
  213. }
  214. [rs close];
  215. }
  216. else {
  217. NSLog(@"Can't find compass image..");
  218. }
  219. // test out the convenience methods in +Additions
  220. [db executeUpdate:@"create table t1 (a integer)"];
  221. [db executeUpdate:@"insert into t1 values (?)", [NSNumber numberWithInt:5]];
  222. NSLog(@"Count of changes (should be 1): %d", [db changes]);
  223. FMDBQuickCheck([db changes] == 1);
  224. int ia = [db intForQuery:@"select a from t1 where a = ?", [NSNumber numberWithInt:5]];
  225. if (ia != 5) {
  226. NSLog(@"intForQuery didn't work (a != 5)");
  227. }
  228. // test the busy rety timeout schtuff.
  229. [db setBusyRetryTimeout:500];
  230. FMDatabase *newDb = [FMDatabase databaseWithPath:dbPath];
  231. [newDb open];
  232. rs = [newDb executeQuery:@"select rowid,* from test where a = ?", @"hi'"];
  233. [rs next]; // just grab one... which will keep the db locked.
  234. NSLog(@"Testing the busy timeout");
  235. BOOL success = [db executeUpdate:@"insert into t1 values (5)"];
  236. if (success) {
  237. NSLog(@"Whoa- the database didn't stay locked!");
  238. return 7;
  239. }
  240. else {
  241. NSLog(@"Hurray, our timeout worked");
  242. }
  243. [rs close];
  244. [newDb close];
  245. success = [db executeUpdate:@"insert into t1 values (5)"];
  246. if (!success) {
  247. NSLog(@"Whoa- the database shouldn't be locked!");
  248. return 8;
  249. }
  250. else {
  251. NSLog(@"Hurray, we can insert again!");
  252. }
  253. // test some nullness.
  254. [db executeUpdate:@"create table t2 (a integer, b integer)"];
  255. if (![db executeUpdate:@"insert into t2 values (?, ?)", nil, [NSNumber numberWithInt:5]]) {
  256. NSLog(@"UH OH, can't insert a nil value for some reason...");
  257. }
  258. rs = [db executeQuery:@"select * from t2"];
  259. while ([rs next]) {
  260. NSString *aa = [rs stringForColumnIndex:0];
  261. NSString *b = [rs stringForColumnIndex:1];
  262. if (aa != nil) {
  263. NSLog(@"%s:%d", __FUNCTION__, __LINE__);
  264. NSLog(@"OH OH, PROBLEMO!");
  265. return 10;
  266. }
  267. else {
  268. NSLog(@"YAY, NULL VALUES");
  269. }
  270. if (![b isEqualToString:@"5"]) {
  271. NSLog(@"%s:%d", __FUNCTION__, __LINE__);
  272. NSLog(@"OH OH, PROBLEMO!");
  273. return 10;
  274. }
  275. }
  276. // test some inner loop funkness.
  277. [db executeUpdate:@"create table t3 (a somevalue)"];
  278. // do it again, just because
  279. [db beginTransaction];
  280. i = 0;
  281. while (i++ < 20) {
  282. [db executeUpdate:@"insert into t3 (a) values (?)" , [NSNumber numberWithInt:i]];
  283. }
  284. [db commit];
  285. rs = [db executeQuery:@"select * from t3"];
  286. while ([rs next]) {
  287. int foo = [rs intForColumnIndex:0];
  288. int newVal = foo + 100;
  289. [db executeUpdate:@"update t3 set a = ? where a = ?" , [NSNumber numberWithInt:newVal], [NSNumber numberWithInt:foo]];
  290. FMResultSet *rs2 = [db executeQuery:@"select a from t3 where a = ?", [NSNumber numberWithInt:newVal]];
  291. [rs2 next];
  292. if ([rs2 intForColumnIndex:0] != newVal) {
  293. NSLog(@"Oh crap, our update didn't work out!");
  294. return 9;
  295. }
  296. [rs2 close];
  297. }
  298. // NSNull tests
  299. [db executeUpdate:@"create table nulltest (a text, b text)"];
  300. [db executeUpdate:@"insert into nulltest (a, b) values (?, ?)" , [NSNull null], @"a"];
  301. [db executeUpdate:@"insert into nulltest (a, b) values (?, ?)" , nil, @"b"];
  302. rs = [db executeQuery:@"select * from nulltest"];
  303. while ([rs next]) {
  304. NSString *a = [rs stringForColumnIndex:0];
  305. NSString *b = [rs stringForColumnIndex:1];
  306. if (!b) {
  307. NSLog(@"Oh crap, the nil / null inserts didn't work!");
  308. return 10;
  309. }
  310. if (a) {
  311. NSLog(@"Oh crap, the nil / null inserts didn't work (son of error message)!");
  312. return 11;
  313. }
  314. else {
  315. NSLog(@"HURRAH FOR NSNULL (and nil)!");
  316. }
  317. }
  318. FMDBQuickCheck([db columnExists:@"a" inTableWithName:@"nulltest"]);
  319. FMDBQuickCheck([db columnExists:@"b" inTableWithName:@"nulltest"]);
  320. FMDBQuickCheck(![db columnExists:@"c" inTableWithName:@"nulltest"]);
  321. // null dates
  322. NSDate *date = [NSDate date];
  323. [db executeUpdate:@"create table datetest (a double, b double, c double)"];
  324. [db executeUpdate:@"insert into datetest (a, b, c) values (?, ?, 0)" , [NSNull null], date];
  325. rs = [db executeQuery:@"select * from datetest"];
  326. while ([rs next]) {
  327. NSDate *a = [rs dateForColumnIndex:0];
  328. NSDate *b = [rs dateForColumnIndex:1];
  329. NSDate *c = [rs dateForColumnIndex:2];
  330. if (a) {
  331. NSLog(@"Oh crap, the null date insert didn't work!");
  332. return 12;
  333. }
  334. if (!c) {
  335. NSLog(@"Oh crap, the 0 date insert didn't work!");
  336. return 12;
  337. }
  338. NSTimeInterval dti = fabs([b timeIntervalSinceDate:date]);
  339. if (floor(dti) > 0.0) {
  340. NSLog(@"Date matches didn't really happen... time difference of %f", dti);
  341. return 13;
  342. }
  343. dti = fabs([c timeIntervalSinceDate:[NSDate dateWithTimeIntervalSince1970:0]]);
  344. if (floor(dti) > 0.0) {
  345. NSLog(@"Date matches didn't really happen... time difference of %f", dti);
  346. return 13;
  347. }
  348. }
  349. NSDate *foo = [db dateForQuery:@"select b from datetest where c = 0"];
  350. assert(foo);
  351. NSTimeInterval dti = fabs([foo timeIntervalSinceDate:date]);
  352. if (floor(dti) > 0.0) {
  353. NSLog(@"Date matches didn't really happen... time difference of %f", dti);
  354. return 14;
  355. }
  356. [db executeUpdate:@"create table nulltest2 (s text, d data, i integer, f double, b integer)"];
  357. // grab the data for this again, since we overwrote it with some memory that has since disapeared.
  358. safariCompass = [NSData dataWithContentsOfFile:@"/Applications/Safari.app/Contents/Resources/compass.icns"];
  359. [db executeUpdate:@"insert into nulltest2 (s, d, i, f, b) values (?, ?, ?, ?, ?)" , @"Hi", safariCompass, [NSNumber numberWithInt:12], [NSNumber numberWithFloat:4.4f], [NSNumber numberWithBool:YES]];
  360. [db executeUpdate:@"insert into nulltest2 (s, d, i, f, b) values (?, ?, ?, ?, ?)" , nil, nil, nil, nil, [NSNull null]];
  361. rs = [db executeQuery:@"select * from nulltest2"];
  362. while ([rs next]) {
  363. i = [rs intForColumnIndex:2];
  364. if (i == 12) {
  365. // it's the first row we inserted.
  366. FMDBQuickCheck(![rs columnIndexIsNull:0]);
  367. FMDBQuickCheck(![rs columnIndexIsNull:1]);
  368. FMDBQuickCheck(![rs columnIndexIsNull:2]);
  369. FMDBQuickCheck(![rs columnIndexIsNull:3]);
  370. FMDBQuickCheck(![rs columnIndexIsNull:4]);
  371. FMDBQuickCheck( [rs columnIndexIsNull:5]);
  372. FMDBQuickCheck([[rs dataForColumn:@"d"] length] == [safariCompass length]);
  373. FMDBQuickCheck(![rs dataForColumn:@"notthere"]);
  374. FMDBQuickCheck(![rs stringForColumnIndex:-2]);
  375. FMDBQuickCheck([rs boolForColumnIndex:4]);
  376. FMDBQuickCheck([rs boolForColumn:@"b"]);
  377. FMDBQuickCheck(fabs(4.4 - [rs doubleForColumn:@"f"]) < 0.0000001);
  378. FMDBQuickCheck(12 == [rs intForColumn:@"i"]);
  379. FMDBQuickCheck(12 == [rs intForColumnIndex:2]);
  380. FMDBQuickCheck(0 == [rs intForColumnIndex:12]); // there is no 12
  381. FMDBQuickCheck(0 == [rs intForColumn:@"notthere"]);
  382. FMDBQuickCheck(12 == [rs longForColumn:@"i"]);
  383. FMDBQuickCheck(12 == [rs longLongIntForColumn:@"i"]);
  384. }
  385. else {
  386. // let's test various null things.
  387. FMDBQuickCheck([rs columnIndexIsNull:0]);
  388. FMDBQuickCheck([rs columnIndexIsNull:1]);
  389. FMDBQuickCheck([rs columnIndexIsNull:2]);
  390. FMDBQuickCheck([rs columnIndexIsNull:3]);
  391. FMDBQuickCheck([rs columnIndexIsNull:4]);
  392. FMDBQuickCheck([rs columnIndexIsNull:5]);
  393. FMDBQuickCheck(![rs dataForColumn:@"d"]);
  394. }
  395. }
  396. {
  397. [db executeUpdate:@"create table utest (a text)"];
  398. [db executeUpdate:@"insert into utest values (?)", @"/übertest"];
  399. rs = [db executeQuery:@"select * from utest where a = ?", @"/übertest"];
  400. FMDBQuickCheck([rs next]);
  401. [rs close];
  402. }
  403. {
  404. [db executeUpdate:@"create table testOneHundredTwelvePointTwo (a text, b integer)"];
  405. [db executeUpdate:@"insert into testOneHundredTwelvePointTwo values (?, ?)" withArgumentsInArray:[NSArray arrayWithObjects:@"one", [NSNumber numberWithInteger:2], nil]];
  406. [db executeUpdate:@"insert into testOneHundredTwelvePointTwo values (?, ?)" withArgumentsInArray:[NSArray arrayWithObjects:@"one", [NSNumber numberWithInteger:3], nil]];
  407. rs = [db executeQuery:@"select * from testOneHundredTwelvePointTwo where b > ?" withArgumentsInArray:[NSArray arrayWithObject:[NSNumber numberWithInteger:1]]];
  408. FMDBQuickCheck([rs next]);
  409. FMDBQuickCheck([rs hasAnotherRow]);
  410. FMDBQuickCheck(![db hadError]);
  411. FMDBQuickCheck([[rs stringForColumnIndex:0] isEqualToString:@"one"]);
  412. FMDBQuickCheck([rs intForColumnIndex:1] == 2);
  413. FMDBQuickCheck([rs next]);
  414. FMDBQuickCheck([rs intForColumnIndex:1] == 3);
  415. FMDBQuickCheck(![rs next]);
  416. FMDBQuickCheck(![rs hasAnotherRow]);
  417. }
  418. {
  419. FMDBQuickCheck([db executeUpdate:@"create table t4 (a text, b text)"]);
  420. FMDBQuickCheck(([db executeUpdate:@"insert into t4 (a, b) values (?, ?)", @"one", @"two"]));
  421. rs = [db executeQuery:@"select t4.a as 't4.a', t4.b from t4;"];
  422. FMDBQuickCheck((rs != nil));
  423. [rs next];
  424. FMDBQuickCheck([[rs stringForColumn:@"t4.a"] isEqualToString:@"one"]);
  425. FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"two"]);
  426. FMDBQuickCheck(strcmp((const char*)[rs UTF8StringForColumnName:@"b"], "two") == 0);
  427. [rs close];
  428. // let's try these again, with the withArgumentsInArray: variation
  429. FMDBQuickCheck([db executeUpdate:@"drop table t4;" withArgumentsInArray:[NSArray array]]);
  430. FMDBQuickCheck([db executeUpdate:@"create table t4 (a text, b text)" withArgumentsInArray:[NSArray array]]);
  431. FMDBQuickCheck(([db executeUpdate:@"insert into t4 (a, b) values (?, ?)" withArgumentsInArray:[NSArray arrayWithObjects:@"one", @"two", nil]]));
  432. rs = [db executeQuery:@"select t4.a as 't4.a', t4.b from t4;" withArgumentsInArray:[NSArray array]];
  433. FMDBQuickCheck((rs != nil));
  434. [rs next];
  435. FMDBQuickCheck([[rs stringForColumn:@"t4.a"] isEqualToString:@"one"]);
  436. FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"two"]);
  437. FMDBQuickCheck(strcmp((const char*)[rs UTF8StringForColumnName:@"b"], "two") == 0);
  438. [rs close];
  439. }
  440. {
  441. FMDBQuickCheck([db tableExists:@"t4"]);
  442. FMDBQuickCheck(![db tableExists:@"thisdoesntexist"]);
  443. rs = [db getSchema];
  444. while ([rs next]) {
  445. FMDBQuickCheck([[rs stringForColumn:@"type"] isEqualToString:@"table"]);
  446. }
  447. }
  448. {
  449. FMDBQuickCheck([db executeUpdate:@"create table t5 (a text, b int, c blob, d text, e text)"]);
  450. FMDBQuickCheck(([db executeUpdateWithFormat:@"insert into t5 values (%s, %d, %@, %c, %lld)", "text", 42, @"BLOB", 'd', 12345678901234ll]));
  451. rs = [db executeQueryWithFormat:@"select * from t5 where a = %s and a = %@ and b = %d", "text", @"text", 42];
  452. FMDBQuickCheck((rs != nil));
  453. [rs next];
  454. FMDBQuickCheck([[rs stringForColumn:@"a"] isEqualToString:@"text"]);
  455. FMDBQuickCheck(([rs intForColumn:@"b"] == 42));
  456. FMDBQuickCheck([[rs stringForColumn:@"c"] isEqualToString:@"BLOB"]);
  457. FMDBQuickCheck([[rs stringForColumn:@"d"] isEqualToString:@"d"]);
  458. FMDBQuickCheck(([rs longLongIntForColumn:@"e"] == 12345678901234));
  459. [rs close];
  460. }
  461. {
  462. FMDBQuickCheck([db executeUpdate:@"create table t55 (a text, b int, c float)"]);
  463. short testShort = -4;
  464. float testFloat = 5.5;
  465. FMDBQuickCheck(([db executeUpdateWithFormat:@"insert into t55 values (%c, %hi, %g)", 'a', testShort, testFloat]));
  466. unsigned short testUShort = 6;
  467. FMDBQuickCheck(([db executeUpdateWithFormat:@"insert into t55 values (%c, %hu, %g)", 'a', testUShort, testFloat]));
  468. rs = [db executeQueryWithFormat:@"select * from t55 where a = %s order by 2", "a"];
  469. FMDBQuickCheck((rs != nil));
  470. [rs next];
  471. FMDBQuickCheck([[rs stringForColumn:@"a"] isEqualToString:@"a"]);
  472. FMDBQuickCheck(([rs intForColumn:@"b"] == -4));
  473. FMDBQuickCheck([[rs stringForColumn:@"c"] isEqualToString:@"5.5"]);
  474. [rs next];
  475. FMDBQuickCheck([[rs stringForColumn:@"a"] isEqualToString:@"a"]);
  476. FMDBQuickCheck(([rs intForColumn:@"b"] == 6));
  477. FMDBQuickCheck([[rs stringForColumn:@"c"] isEqualToString:@"5.5"]);
  478. [rs close];
  479. }
  480. {
  481. FMDBQuickCheck([db executeUpdate:@"create table tatwhat (a text)"]);
  482. BOOL worked = [db executeUpdateWithFormat:@"insert into tatwhat values(%@)", nil];
  483. FMDBQuickCheck(worked);
  484. rs = [db executeQueryWithFormat:@"select * from tatwhat"];
  485. FMDBQuickCheck((rs != nil));
  486. FMDBQuickCheck(([rs next]));
  487. FMDBQuickCheck([rs columnIndexIsNull:0]);
  488. FMDBQuickCheck((![rs next]));
  489. }
  490. {
  491. FMDBQuickCheck(([db update:@"insert into t5 values (?, ?, ?, ?, ?)" withErrorAndBindings:&err, @"text", [NSNumber numberWithInt:42], @"BLOB", @"d", [NSNumber numberWithInt:0]]));
  492. }
  493. {
  494. rs = [db executeQuery:@"select * from t5 where a=?" withArgumentsInArray:@[]];
  495. FMDBQuickCheck((![rs next]));
  496. }
  497. // test attach for the heck of it.
  498. {
  499. //FMDatabase *dbA = [FMDatabase databaseWithPath:dbPath];
  500. [fileManager removeItemAtPath:@"/tmp/attachme.db" error:nil];
  501. FMDatabase *dbB = [FMDatabase databaseWithPath:@"/tmp/attachme.db"];
  502. FMDBQuickCheck([dbB open]);
  503. FMDBQuickCheck([dbB executeUpdate:@"create table attached (a text)"]);
  504. FMDBQuickCheck(([dbB executeUpdate:@"insert into attached values (?)", @"test"]));
  505. FMDBQuickCheck([dbB close]);
  506. [db executeUpdate:@"attach database '/tmp/attachme.db' as attack"];
  507. rs = [db executeQuery:@"select * from attack.attached"];
  508. FMDBQuickCheck([rs next]);
  509. [rs close];
  510. }
  511. {
  512. // -------------------------------------------------------------------------------
  513. // Named parameters.
  514. FMDBQuickCheck([db executeUpdate:@"create table namedparamtest (a text, b text, c integer, d double)"]);
  515. NSMutableDictionary *dictionaryArgs = [NSMutableDictionary dictionary];
  516. [dictionaryArgs setObject:@"Text1" forKey:@"a"];
  517. [dictionaryArgs setObject:@"Text2" forKey:@"b"];
  518. [dictionaryArgs setObject:[NSNumber numberWithInt:1] forKey:@"c"];
  519. [dictionaryArgs setObject:[NSNumber numberWithDouble:2.0] forKey:@"d"];
  520. FMDBQuickCheck([db executeUpdate:@"insert into namedparamtest values (:a, :b, :c, :d)" withParameterDictionary:dictionaryArgs]);
  521. rs = [db executeQuery:@"select * from namedparamtest"];
  522. FMDBQuickCheck((rs != nil));
  523. [rs next];
  524. FMDBQuickCheck([[rs stringForColumn:@"a"] isEqualToString:@"Text1"]);
  525. FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"Text2"]);
  526. FMDBQuickCheck([rs intForColumn:@"c"] == 1);
  527. FMDBQuickCheck([rs doubleForColumn:@"d"] == 2.0);
  528. [rs close];
  529. dictionaryArgs = [NSMutableDictionary dictionary];
  530. [dictionaryArgs setObject:@"Text2" forKey:@"blah"];
  531. rs = [db executeQuery:@"select * from namedparamtest where b = :blah" withParameterDictionary:dictionaryArgs];
  532. FMDBQuickCheck((rs != nil));
  533. FMDBQuickCheck([rs next]);
  534. FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"Text2"]);
  535. [rs close];
  536. }
  537. // just for fun.
  538. rs = [db executeQuery:@"PRAGMA database_list"];
  539. while ([rs next]) {
  540. NSString *file = [rs stringForColumn:@"file"];
  541. NSLog(@"database_list: %@", file);
  542. }
  543. // print out some stats if we are using cached statements.
  544. if ([db shouldCacheStatements]) {
  545. NSEnumerator *e = [[db cachedStatements] objectEnumerator];;
  546. FMStatement *statement;
  547. while ((statement = [e nextObject])) {
  548. NSLog(@"%@", statement);
  549. }
  550. }
  551. [db setShouldCacheStatements:true];
  552. [db executeUpdate:@"CREATE TABLE testCacheStatements(key INTEGER PRIMARY KEY, value INTEGER)"];
  553. [db executeUpdate:@"INSERT INTO testCacheStatements (key, value) VALUES (1, 2)"];
  554. [db executeUpdate:@"INSERT INTO testCacheStatements (key, value) VALUES (2, 4)"];
  555. FMDBQuickCheck([[db executeQuery:@"SELECT * FROM testCacheStatements WHERE key=1"] next]);
  556. FMDBQuickCheck([[db executeQuery:@"SELECT * FROM testCacheStatements WHERE key=1"] next]);
  557. [db close];
  558. testPool(dbPath);
  559. testDateFormat();
  560. FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
  561. FMDBQuickCheck(queue);
  562. {
  563. [queue inDatabase:^(FMDatabase *adb) {
  564. [adb executeUpdate:@"create table qfoo (foo text)"];
  565. [adb executeUpdate:@"insert into qfoo values ('hi')"];
  566. [adb executeUpdate:@"insert into qfoo values ('hello')"];
  567. [adb executeUpdate:@"insert into qfoo values ('not')"];
  568. int count = 0;
  569. FMResultSet *rsl = [adb executeQuery:@"select * from qfoo where foo like 'h%'"];
  570. while ([rsl next]) {
  571. count++;
  572. }
  573. FMDBQuickCheck(count == 2);
  574. count = 0;
  575. rsl = [adb executeQuery:@"select * from qfoo where foo like ?", @"h%"];
  576. while ([rsl next]) {
  577. count++;
  578. }
  579. FMDBQuickCheck(count == 2);
  580. }];
  581. }
  582. {
  583. // You should see pairs of numbers show up in stdout for this stuff:
  584. size_t ops = 16;
  585. dispatch_queue_t dqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  586. dispatch_apply(ops, dqueue, ^(size_t nby) {
  587. // just mix things up a bit for demonstration purposes.
  588. if (nby % 2 == 1) {
  589. [NSThread sleepForTimeInterval:.1];
  590. [queue inTransaction:^(FMDatabase *adb, BOOL *rollback) {
  591. NSLog(@"Starting query %ld", nby);
  592. FMResultSet *rsl = [adb executeQuery:@"select * from qfoo where foo like 'h%'"];
  593. while ([rsl next]) {
  594. ;// whatever.
  595. }
  596. NSLog(@"Ending query %ld", nby);
  597. }];
  598. }
  599. if (nby % 3 == 1) {
  600. [NSThread sleepForTimeInterval:.1];
  601. }
  602. [queue inTransaction:^(FMDatabase *adb, BOOL *rollback) {
  603. NSLog(@"Starting update %ld", nby);
  604. [adb executeUpdate:@"insert into qfoo values ('1')"];
  605. [adb executeUpdate:@"insert into qfoo values ('2')"];
  606. [adb executeUpdate:@"insert into qfoo values ('3')"];
  607. NSLog(@"Ending update %ld", nby);
  608. }];
  609. });
  610. [queue close];
  611. [queue inDatabase:^(FMDatabase *adb) {
  612. FMDBQuickCheck([adb executeUpdate:@"insert into qfoo values ('1')"]);
  613. }];
  614. }
  615. {
  616. [queue inDatabase:^(FMDatabase *adb) {
  617. [adb executeUpdate:@"create table colNameTest (a, b, c, d)"];
  618. FMDBQuickCheck([adb executeUpdate:@"insert into colNameTest values (1, 2, 3, 4)"]);
  619. FMResultSet *ars = [adb executeQuery:@"select * from colNameTest"];
  620. NSDictionary *d = [ars columnNameToIndexMap];
  621. FMDBQuickCheck([d count] == 4);
  622. FMDBQuickCheck([[d objectForKey:@"a"] intValue] == 0);
  623. FMDBQuickCheck([[d objectForKey:@"b"] intValue] == 1);
  624. FMDBQuickCheck([[d objectForKey:@"c"] intValue] == 2);
  625. FMDBQuickCheck([[d objectForKey:@"d"] intValue] == 3);
  626. [ars close];
  627. }];
  628. }
  629. {
  630. [queue inDatabase:^(FMDatabase *adb) {
  631. [adb executeUpdate:@"create table transtest (a integer)"];
  632. FMDBQuickCheck([adb executeUpdate:@"insert into transtest values (1)"]);
  633. FMDBQuickCheck([adb executeUpdate:@"insert into transtest values (2)"]);
  634. int rowCount = 0;
  635. FMResultSet *ars = [adb executeQuery:@"select * from transtest"];
  636. while ([ars next]) {
  637. rowCount++;
  638. }
  639. FMDBQuickCheck(rowCount == 2);
  640. }];
  641. [queue inTransaction:^(FMDatabase *adb, BOOL *rollback) {
  642. FMDBQuickCheck([adb executeUpdate:@"insert into transtest values (3)"]);
  643. if (YES) {
  644. // uh oh!, something went wrong (not really, this is just a test
  645. *rollback = YES;
  646. return;
  647. }
  648. FMDBQuickCheck([adb executeUpdate:@"insert into transtest values (4)"]);
  649. }];
  650. [queue inDatabase:^(FMDatabase *adb) {
  651. int rowCount = 0;
  652. FMResultSet *ars = [adb executeQuery:@"select * from transtest"];
  653. while ([ars next]) {
  654. rowCount++;
  655. }
  656. FMDBQuickCheck(![adb hasOpenResultSets]);
  657. NSLog(@"after rollback, rowCount is %d (should be 2)", rowCount);
  658. FMDBQuickCheck(rowCount == 2);
  659. }];
  660. }
  661. // hey, let's make a custom function!
  662. [queue inDatabase:^(FMDatabase *adb) {
  663. [adb executeUpdate:@"create table ftest (foo text)"];
  664. [adb executeUpdate:@"insert into ftest values ('hello')"];
  665. [adb executeUpdate:@"insert into ftest values ('hi')"];
  666. [adb executeUpdate:@"insert into ftest values ('not h!')"];
  667. [adb executeUpdate:@"insert into ftest values ('definitely not h!')"];
  668. [adb makeFunctionNamed:@"StringStartsWithH" maximumArguments:1 withBlock:^(sqlite3_context *context, int aargc, sqlite3_value **aargv) {
  669. if (sqlite3_value_type(aargv[0]) == SQLITE_TEXT) {
  670. @autoreleasepool {
  671. const char *c = (const char *)sqlite3_value_text(aargv[0]);
  672. NSString *s = [NSString stringWithUTF8String:c];
  673. sqlite3_result_int(context, [s hasPrefix:@"h"]);
  674. }
  675. }
  676. else {
  677. NSLog(@"Unknown formart for StringStartsWithH (%d) %s:%d", sqlite3_value_type(aargv[0]), __FUNCTION__, __LINE__);
  678. sqlite3_result_null(context);
  679. }
  680. }];
  681. int rowCount = 0;
  682. FMResultSet *ars = [adb executeQuery:@"select * from ftest where StringStartsWithH(foo)"];
  683. while ([ars next]) {
  684. rowCount++;
  685. NSLog(@"Does %@ start with 'h'?", [rs stringForColumnIndex:0]);
  686. }
  687. FMDBQuickCheck(rowCount == 2);
  688. }];
  689. NSLog(@"That was version %@ of sqlite", [FMDatabase sqliteLibVersion]);
  690. }// this is the end of our @autorelease pool.
  691. return 0;
  692. }
  693. /*
  694. Test the various FMDatabasePool things.
  695. */
  696. void testPool(NSString *dbPath) {
  697. FMDatabasePool *dbPool = [FMDatabasePool databasePoolWithPath:dbPath];
  698. FMDBQuickCheck([dbPool countOfOpenDatabases] == 0);
  699. __block FMDatabase *db1;
  700. [dbPool inDatabase:^(FMDatabase *db) {
  701. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  702. FMDBQuickCheck([db tableExists:@"t4"]);
  703. db1 = db;
  704. }];
  705. [dbPool inDatabase:^(FMDatabase *db) {
  706. FMDBQuickCheck(db1 == db);
  707. [dbPool inDatabase:^(FMDatabase *db2) {
  708. FMDBQuickCheck(db2 != db);
  709. }];
  710. }];
  711. FMDBQuickCheck([dbPool countOfOpenDatabases] == 2);
  712. [dbPool inDatabase:^(FMDatabase *db) {
  713. [db executeUpdate:@"create table easy (a text)"];
  714. [db executeUpdate:@"create table easy2 (a text)"];
  715. }];
  716. FMDBQuickCheck([dbPool countOfOpenDatabases] == 2);
  717. [dbPool releaseAllDatabases];
  718. FMDBQuickCheck([dbPool countOfOpenDatabases] == 0);
  719. [dbPool inDatabase:^(FMDatabase *aDb) {
  720. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 0);
  721. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 1);
  722. FMDBQuickCheck([aDb tableExists:@"t4"]);
  723. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 0);
  724. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 1);
  725. FMDBQuickCheck(([aDb executeUpdate:@"insert into easy (a) values (?)", @"hi"]));
  726. // just for fun.
  727. FMResultSet *rs2 = [aDb executeQuery:@"select * from easy"];
  728. FMDBQuickCheck([rs2 next]);
  729. while ([rs2 next]) { ; } // whatevers.
  730. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  731. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 0);
  732. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 1);
  733. }];
  734. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  735. {
  736. [dbPool inDatabase:^(FMDatabase *db) {
  737. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1]];
  738. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:2]];
  739. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:3]];
  740. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 0);
  741. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 1);
  742. }];
  743. }
  744. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  745. [dbPool setMaximumNumberOfDatabasesToCreate:2];
  746. [dbPool inDatabase:^(FMDatabase *db) {
  747. [dbPool inDatabase:^(FMDatabase *db2) {
  748. [dbPool inDatabase:^(FMDatabase *db3) {
  749. FMDBQuickCheck([dbPool countOfOpenDatabases] == 2);
  750. FMDBQuickCheck(!db3);
  751. }];
  752. }];
  753. }];
  754. [dbPool setMaximumNumberOfDatabasesToCreate:0];
  755. [dbPool releaseAllDatabases];
  756. FMDBQuickCheck([dbPool countOfOpenDatabases] == 0);
  757. [dbPool inDatabase:^(FMDatabase *db) {
  758. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:3]];
  759. }];
  760. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  761. [dbPool inTransaction:^(FMDatabase *adb, BOOL *rollback) {
  762. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1001]];
  763. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1002]];
  764. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1003]];
  765. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  766. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 0);
  767. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 1);
  768. }];
  769. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  770. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 1);
  771. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 0);
  772. [dbPool inDatabase:^(FMDatabase *db) {
  773. FMResultSet *rs2 = [db executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1001]];
  774. FMDBQuickCheck([rs2 next]);
  775. FMDBQuickCheck(![rs2 next]);
  776. }];
  777. [dbPool inDeferredTransaction:^(FMDatabase *adb, BOOL *rollback) {
  778. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1004]];
  779. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1005]];
  780. *rollback = YES;
  781. }];
  782. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  783. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 1);
  784. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 0);
  785. NSError *err = [dbPool inSavePoint:^(FMDatabase *db, BOOL *rollback) {
  786. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1006]];
  787. }];
  788. FMDBQuickCheck(!err);
  789. {
  790. err = [dbPool inSavePoint:^(FMDatabase *adb, BOOL *rollback) {
  791. FMDBQuickCheck(![adb hadError]);
  792. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1009]];
  793. [adb inSavePoint:^(BOOL *arollback) {
  794. FMDBQuickCheck(([adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1010]]));
  795. *arollback = YES;
  796. }];
  797. }];
  798. FMDBQuickCheck(!err);
  799. [dbPool inDatabase:^(FMDatabase *db) {
  800. FMResultSet *rs2 = [db executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1009]];
  801. FMDBQuickCheck([rs2 next]);
  802. FMDBQuickCheck(![rs2 next]); // close it out.
  803. rs2 = [db executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1010]];
  804. FMDBQuickCheck(![rs2 next]);
  805. }];
  806. }
  807. {
  808. [dbPool inDatabase:^(FMDatabase *db) {
  809. [db executeUpdate:@"create table likefoo (foo text)"];
  810. [db executeUpdate:@"insert into likefoo values ('hi')"];
  811. [db executeUpdate:@"insert into likefoo values ('hello')"];
  812. [db executeUpdate:@"insert into likefoo values ('not')"];
  813. int count = 0;
  814. FMResultSet *rsl = [db executeQuery:@"select * from likefoo where foo like 'h%'"];
  815. while ([rsl next]) {
  816. count++;
  817. }
  818. FMDBQuickCheck(count == 2);
  819. count = 0;
  820. rsl = [db executeQuery:@"select * from likefoo where foo like ?", @"h%"];
  821. while ([rsl next]) {
  822. count++;
  823. }
  824. FMDBQuickCheck(count == 2);
  825. }];
  826. }
  827. {
  828. size_t ops = 128;
  829. dispatch_queue_t dqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  830. dispatch_apply(ops, dqueue, ^(size_t nby) {
  831. // just mix things up a bit for demonstration purposes.
  832. if (nby % 2 == 1) {
  833. [NSThread sleepForTimeInterval:.1];
  834. }
  835. [dbPool inDatabase:^(FMDatabase *db) {
  836. NSLog(@"Starting query %ld", nby);
  837. FMResultSet *rsl = [db executeQuery:@"select * from likefoo where foo like 'h%'"];
  838. while ([rsl next]) {
  839. if (nby % 3 == 1) {
  840. [NSThread sleepForTimeInterval:.05];
  841. }
  842. }
  843. NSLog(@"Ending query %ld", nby);
  844. }];
  845. });
  846. NSLog(@"Number of open databases after crazy gcd stuff: %ld", [dbPool countOfOpenDatabases]);
  847. }
  848. // if you want to see a deadlock, just uncomment this line and run:
  849. //#define ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD 1
  850. #ifdef ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD
  851. {
  852. int ops = 16;
  853. dispatch_queue_t dqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  854. dispatch_apply(ops, dqueue, ^(size_t nby) {
  855. // just mix things up a bit for demonstration purposes.
  856. if (nby % 2 == 1) {
  857. [NSThread sleepForTimeInterval:.1];
  858. [dbPool inTransaction:^(FMDatabase *db, BOOL *rollback) {
  859. NSLog(@"Starting query %ld", nby);
  860. FMResultSet *rsl = [db executeQuery:@"select * from likefoo where foo like 'h%'"];
  861. while ([rsl next]) {
  862. ;// whatever.
  863. }
  864. NSLog(@"Ending query %ld", nby);
  865. }];
  866. }
  867. if (nby % 3 == 1) {
  868. [NSThread sleepForTimeInterval:.1];
  869. }
  870. [dbPool inTransaction:^(FMDatabase *db, BOOL *rollback) {
  871. NSLog(@"Starting update %ld", nby);
  872. [db executeUpdate:@"insert into likefoo values ('1')"];
  873. [db executeUpdate:@"insert into likefoo values ('2')"];
  874. [db executeUpdate:@"insert into likefoo values ('3')"];
  875. NSLog(@"Ending update %ld", nby);
  876. }];
  877. });
  878. [dbPool releaseAllDatabases];
  879. [dbPool inDatabase:^(FMDatabase *db) {
  880. FMDBQuickCheck([db executeUpdate:@"insert into likefoo values ('1')"]);
  881. }];
  882. }
  883. #endif
  884. }
  885. /*
  886. Test the date format
  887. */
  888. void testOneDateFormat( FMDatabase *db, NSDate *testDate ) {
  889. [db executeUpdate:@"DROP TABLE IF EXISTS test_format"];
  890. [db executeUpdate:@"CREATE TABLE test_format ( test TEXT )"];
  891. [db executeUpdate:@"INSERT INTO test_format(test) VALUES (?)", testDate];
  892. FMResultSet *rs = [db executeQuery:@"SELECT test FROM test_format"];
  893. if ([rs next]) {
  894. NSDate *found = [rs dateForColumnIndex:0];
  895. if (NSOrderedSame != [testDate compare:found]) {
  896. NSLog(@"Did not get back what we stored.");
  897. }
  898. }
  899. else {
  900. NSLog(@"Insertion borked");
  901. }
  902. [rs close];
  903. }
  904. void testDateFormat() {
  905. FMDatabase *db = [FMDatabase databaseWithPath:nil]; // use in-memory DB
  906. [db open];
  907. NSDateFormatter *fmt = [FMDatabase storeableDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  908. NSDate *testDate = [fmt dateFromString:@"2013-02-20 12:00:00"];
  909. // test timestamp dates (ensuring our change does not break those)
  910. testOneDateFormat(db,testDate);
  911. // now test the string-based timestamp
  912. [db setDateFormat:fmt];
  913. testOneDateFormat(db, testDate);
  914. [db close];
  915. }
  916. /*
  917. What is this function for? Think of it as a template which a developer can use
  918. to report bugs.
  919. If you have a bug, make it reproduce in this function and then let the
  920. developer(s) know either via the github bug reporter or the mailing list.
  921. */
  922. void FMDBReportABugFunction() {
  923. NSString *dbPath = @"/tmp/bugreportsample.db";
  924. // delete the old db if it exists
  925. NSFileManager *fileManager = [NSFileManager defaultManager];
  926. [fileManager removeItemAtPath:dbPath error:nil];
  927. FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
  928. [queue inDatabase:^(FMDatabase *db) {
  929. /*
  930. Change the contents of this block to suit your needs.
  931. */
  932. BOOL worked = [db executeUpdate:@"create table test (a text, b text, c integer, d double, e double)"];
  933. FMDBQuickCheck(worked);
  934. worked = [db executeUpdate:@"insert into test values ('a', 'b', 1, 2.2, 2.3)"];
  935. FMDBQuickCheck(worked);
  936. FMResultSet *rs = [db executeQuery:@"select * from test"];
  937. FMDBQuickCheck([rs next]);
  938. [rs close];
  939. }];
  940. [queue close];
  941. // uncomment the following line if you don't want to run through all the other tests.
  942. //exit(0);
  943. }