fmdb.m 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985
  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. int main (int argc, const char * argv[]) {
  8. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  9. NSString *dbPath = @"/tmp/tmp.db";
  10. // delete the old db.
  11. NSFileManager *fileManager = [NSFileManager defaultManager];
  12. [fileManager removeItemAtPath:dbPath error:nil];
  13. FMDatabase *db = [FMDatabase databaseWithPath:dbPath];
  14. NSLog(@"Is SQLite compiled with it's thread safe options turned on? %@!", [FMDatabase isSQLiteThreadSafe] ? @"Yes" : @"No");
  15. {
  16. // -------------------------------------------------------------------------------
  17. // Un-opened database check.
  18. FMDBQuickCheck([db executeQuery:@"select * from table"] == nil);
  19. NSLog(@"%d: %@", [db lastErrorCode], [db lastErrorMessage]);
  20. }
  21. if (![db open]) {
  22. NSLog(@"Could not open db.");
  23. [pool release];
  24. return 0;
  25. }
  26. // kind of experimentalish.
  27. [db setShouldCacheStatements:YES];
  28. // create a bad statement, just to test the error code.
  29. [db executeUpdate:@"blah blah blah"];
  30. FMDBQuickCheck([db hadError]);
  31. if ([db hadError]) {
  32. NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
  33. }
  34. NSError *err = 0x00;
  35. FMDBQuickCheck(![db update:@"blah blah blah" withErrorAndBindings:&err]);
  36. FMDBQuickCheck(err != nil);
  37. FMDBQuickCheck([err code] == SQLITE_ERROR);
  38. NSLog(@"err: '%@'", err);
  39. // but of course, I don't bother checking the error codes below.
  40. // Bad programmer, no cookie.
  41. [db executeUpdate:@"create table test (a text, b text, c integer, d double, e double)"];
  42. [db beginTransaction];
  43. int i = 0;
  44. while (i++ < 20) {
  45. [db executeUpdate:@"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,
  46. @"hi'", // look! I put in a ', and I'm not escaping it!
  47. [NSString stringWithFormat:@"number %d", i],
  48. [NSNumber numberWithInt:i],
  49. [NSDate date],
  50. [NSNumber numberWithFloat:2.2f]];
  51. }
  52. [db commit];
  53. // do it again, just because
  54. [db beginTransaction];
  55. i = 0;
  56. while (i++ < 20) {
  57. [db executeUpdate:@"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,
  58. @"hi again'", // look! I put in a ', and I'm not escaping it!
  59. [NSString stringWithFormat:@"number %d", i],
  60. [NSNumber numberWithInt:i],
  61. [NSDate date],
  62. [NSNumber numberWithFloat:2.2f]];
  63. }
  64. [db commit];
  65. FMResultSet *rs = [db executeQuery:@"select rowid,* from test where a = ?", @"hi'"];
  66. while ([rs next]) {
  67. // just print out what we've got in a number of formats.
  68. NSLog(@"%d %@ %@ %@ %@ %f %f",
  69. [rs intForColumn:@"c"],
  70. [rs stringForColumn:@"b"],
  71. [rs stringForColumn:@"a"],
  72. [rs stringForColumn:@"rowid"],
  73. [rs dateForColumn:@"d"],
  74. [rs doubleForColumn:@"d"],
  75. [rs doubleForColumn:@"e"]);
  76. if (!([[rs columnNameForIndex:0] isEqualToString:@"rowid"] &&
  77. [[rs columnNameForIndex:1] isEqualToString:@"a"])
  78. ) {
  79. NSLog(@"WHOA THERE BUDDY, columnNameForIndex ISN'T WORKING!");
  80. return 7;
  81. }
  82. }
  83. // close the result set.
  84. // it'll also close when it's dealloc'd, but we're closing the database before
  85. // the autorelease pool closes, so sqlite will complain about it.
  86. [rs close];
  87. // ----------------------------------------------------------------------------------------
  88. // blob support.
  89. [db executeUpdate:@"create table blobTable (a text, b blob)"];
  90. // let's read in an image from safari's app bundle.
  91. NSData *safariCompass = [NSData dataWithContentsOfFile:@"/Applications/Safari.app/Contents/Resources/compass.icns"];
  92. if (safariCompass) {
  93. [db executeUpdate:@"insert into blobTable (a, b) values (?,?)", @"safari's compass", safariCompass];
  94. rs = [db executeQuery:@"select b from blobTable where a = ?", @"safari's compass"];
  95. if ([rs next]) {
  96. safariCompass = [rs dataForColumn:@"b"];
  97. [safariCompass writeToFile:@"/tmp/compass.icns" atomically:NO];
  98. // let's look at our fancy image that we just wrote out..
  99. system("/usr/bin/open /tmp/compass.icns");
  100. // ye shall read the header for this function, or suffer the consequences.
  101. safariCompass = [rs dataNoCopyForColumn:@"b"];
  102. [safariCompass writeToFile:@"/tmp/compass_data_no_copy.icns" atomically:NO];
  103. system("/usr/bin/open /tmp/compass_data_no_copy.icns");
  104. }
  105. else {
  106. NSLog(@"Could not select image.");
  107. }
  108. [rs close];
  109. }
  110. else {
  111. NSLog(@"Can't find compass image..");
  112. }
  113. // test out the convenience methods in +Additions
  114. [db executeUpdate:@"create table t1 (a integer)"];
  115. [db executeUpdate:@"insert into t1 values (?)", [NSNumber numberWithInt:5]];
  116. NSLog(@"Count of changes (should be 1): %d", [db changes]);
  117. FMDBQuickCheck([db changes] == 1);
  118. int a = [db intForQuery:@"select a from t1 where a = ?", [NSNumber numberWithInt:5]];
  119. if (a != 5) {
  120. NSLog(@"intForQuery didn't work (a != 5)");
  121. }
  122. // test the busy rety timeout schtuff.
  123. [db setBusyRetryTimeout:50000];
  124. FMDatabase *newDb = [FMDatabase databaseWithPath:dbPath];
  125. [newDb open];
  126. rs = [newDb executeQuery:@"select rowid,* from test where a = ?", @"hi'"];
  127. [rs next]; // just grab one... which will keep the db locked.
  128. NSLog(@"Testing the busy timeout");
  129. BOOL success = [db executeUpdate:@"insert into t1 values (5)"];
  130. if (success) {
  131. NSLog(@"Whoa- the database didn't stay locked!");
  132. return 7;
  133. }
  134. else {
  135. NSLog(@"Hurray, our timeout worked");
  136. }
  137. [rs close];
  138. [newDb close];
  139. success = [db executeUpdate:@"insert into t1 values (5)"];
  140. if (!success) {
  141. NSLog(@"Whoa- the database shouldn't be locked!");
  142. return 8;
  143. }
  144. else {
  145. NSLog(@"Hurray, we can insert again!");
  146. }
  147. // test some nullness.
  148. [db executeUpdate:@"create table t2 (a integer, b integer)"];
  149. if (![db executeUpdate:@"insert into t2 values (?, ?)", nil, [NSNumber numberWithInt:5]]) {
  150. NSLog(@"UH OH, can't insert a nil value for some reason...");
  151. }
  152. rs = [db executeQuery:@"select * from t2"];
  153. while ([rs next]) {
  154. NSString *a = [rs stringForColumnIndex:0];
  155. NSString *b = [rs stringForColumnIndex:1];
  156. if (a != nil) {
  157. NSLog(@"%s:%d", __FUNCTION__, __LINE__);
  158. NSLog(@"OH OH, PROBLEMO!");
  159. return 10;
  160. }
  161. else {
  162. NSLog(@"YAY, NULL VALUES");
  163. }
  164. if (![b isEqualToString:@"5"]) {
  165. NSLog(@"%s:%d", __FUNCTION__, __LINE__);
  166. NSLog(@"OH OH, PROBLEMO!");
  167. return 10;
  168. }
  169. }
  170. // test some inner loop funkness.
  171. [db executeUpdate:@"create table t3 (a somevalue)"];
  172. // do it again, just because
  173. [db beginTransaction];
  174. i = 0;
  175. while (i++ < 20) {
  176. [db executeUpdate:@"insert into t3 (a) values (?)" , [NSNumber numberWithInt:i]];
  177. }
  178. [db commit];
  179. rs = [db executeQuery:@"select * from t3"];
  180. while ([rs next]) {
  181. int foo = [rs intForColumnIndex:0];
  182. int newVal = foo + 100;
  183. [db executeUpdate:@"update t3 set a = ? where a = ?" , [NSNumber numberWithInt:newVal], [NSNumber numberWithInt:foo]];
  184. FMResultSet *rs2 = [db executeQuery:@"select a from t3 where a = ?", [NSNumber numberWithInt:newVal]];
  185. [rs2 next];
  186. if ([rs2 intForColumnIndex:0] != newVal) {
  187. NSLog(@"Oh crap, our update didn't work out!");
  188. return 9;
  189. }
  190. [rs2 close];
  191. }
  192. // NSNull tests
  193. [db executeUpdate:@"create table nulltest (a text, b text)"];
  194. [db executeUpdate:@"insert into nulltest (a, b) values (?, ?)" , [NSNull null], @"a"];
  195. [db executeUpdate:@"insert into nulltest (a, b) values (?, ?)" , nil, @"b"];
  196. rs = [db executeQuery:@"select * from nulltest"];
  197. while ([rs next]) {
  198. NSString *a = [rs stringForColumnIndex:0];
  199. NSString *b = [rs stringForColumnIndex:1];
  200. if (!b) {
  201. NSLog(@"Oh crap, the nil / null inserts didn't work!");
  202. return 10;
  203. }
  204. if (a) {
  205. NSLog(@"Oh crap, the nil / null inserts didn't work (son of error message)!");
  206. return 11;
  207. }
  208. else {
  209. NSLog(@"HURRAH FOR NSNULL (and nil)!");
  210. }
  211. }
  212. // null dates
  213. NSDate *date = [NSDate date];
  214. [db executeUpdate:@"create table datetest (a double, b double, c double)"];
  215. [db executeUpdate:@"insert into datetest (a, b, c) values (?, ?, 0)" , [NSNull null], date];
  216. rs = [db executeQuery:@"select * from datetest"];
  217. while ([rs next]) {
  218. NSDate *a = [rs dateForColumnIndex:0];
  219. NSDate *b = [rs dateForColumnIndex:1];
  220. NSDate *c = [rs dateForColumnIndex:2];
  221. if (a) {
  222. NSLog(@"Oh crap, the null date insert didn't work!");
  223. return 12;
  224. }
  225. if (!c) {
  226. NSLog(@"Oh crap, the 0 date insert didn't work!");
  227. return 12;
  228. }
  229. NSTimeInterval dti = fabs([b timeIntervalSinceDate:date]);
  230. if (floor(dti) > 0.0) {
  231. NSLog(@"Date matches didn't really happen... time difference of %f", dti);
  232. return 13;
  233. }
  234. dti = fabs([c timeIntervalSinceDate:[NSDate dateWithTimeIntervalSince1970:0]]);
  235. if (floor(dti) > 0.0) {
  236. NSLog(@"Date matches didn't really happen... time difference of %f", dti);
  237. return 13;
  238. }
  239. }
  240. NSDate *foo = [db dateForQuery:@"select b from datetest where c = 0"];
  241. assert(foo);
  242. NSTimeInterval dti = fabs([foo timeIntervalSinceDate:date]);
  243. if (floor(dti) > 0.0) {
  244. NSLog(@"Date matches didn't really happen... time difference of %f", dti);
  245. return 14;
  246. }
  247. [db executeUpdate:@"create table nulltest2 (s text, d data, i integer, f double, b integer)"];
  248. [db executeUpdate:@"insert into nulltest2 (s, d, i, f, b) values (?, ?, ?, ?, ?)" , @"Hi", safariCompass, [NSNumber numberWithInt:12], [NSNumber numberWithFloat:4.4f], [NSNumber numberWithBool:YES]];
  249. [db executeUpdate:@"insert into nulltest2 (s, d, i, f, b) values (?, ?, ?, ?, ?)" , nil, nil, nil, nil, [NSNull null]];
  250. rs = [db executeQuery:@"select * from nulltest2"];
  251. while ([rs next]) {
  252. int i = [rs intForColumnIndex:2];
  253. if (i == 12) {
  254. // it's the first row we inserted.
  255. FMDBQuickCheck(![rs columnIndexIsNull:0]);
  256. FMDBQuickCheck(![rs columnIndexIsNull:1]);
  257. FMDBQuickCheck(![rs columnIndexIsNull:2]);
  258. FMDBQuickCheck(![rs columnIndexIsNull:3]);
  259. FMDBQuickCheck(![rs columnIndexIsNull:4]);
  260. FMDBQuickCheck( [rs columnIndexIsNull:5]);
  261. FMDBQuickCheck([[rs dataForColumn:@"d"] length] == [safariCompass length]);
  262. FMDBQuickCheck(![rs dataForColumn:@"notthere"]);
  263. FMDBQuickCheck(![rs stringForColumnIndex:-2]);
  264. FMDBQuickCheck([rs boolForColumnIndex:4]);
  265. FMDBQuickCheck([rs boolForColumn:@"b"]);
  266. FMDBQuickCheck(fabs(4.4 - [rs doubleForColumn:@"f"]) < 0.0000001);
  267. FMDBQuickCheck(12 == [rs intForColumn:@"i"]);
  268. FMDBQuickCheck(12 == [rs intForColumnIndex:2]);
  269. FMDBQuickCheck(0 == [rs intForColumnIndex:12]); // there is no 12
  270. FMDBQuickCheck(0 == [rs intForColumn:@"notthere"]);
  271. FMDBQuickCheck(12 == [rs longForColumn:@"i"]);
  272. FMDBQuickCheck(12 == [rs longLongIntForColumn:@"i"]);
  273. }
  274. else {
  275. // let's test various null things.
  276. FMDBQuickCheck([rs columnIndexIsNull:0]);
  277. FMDBQuickCheck([rs columnIndexIsNull:1]);
  278. FMDBQuickCheck([rs columnIndexIsNull:2]);
  279. FMDBQuickCheck([rs columnIndexIsNull:3]);
  280. FMDBQuickCheck([rs columnIndexIsNull:4]);
  281. FMDBQuickCheck([rs columnIndexIsNull:5]);
  282. FMDBQuickCheck(![rs dataForColumn:@"d"]);
  283. }
  284. }
  285. {
  286. [db executeUpdate:@"create table testOneHundredTwelvePointTwo (a text, b integer)"];
  287. [db executeUpdate:@"insert into testOneHundredTwelvePointTwo values (?, ?)" withArgumentsInArray:[NSArray arrayWithObjects:@"one", [NSNumber numberWithInteger:2], nil]];
  288. [db executeUpdate:@"insert into testOneHundredTwelvePointTwo values (?, ?)" withArgumentsInArray:[NSArray arrayWithObjects:@"one", [NSNumber numberWithInteger:3], nil]];
  289. rs = [db executeQuery:@"select * from testOneHundredTwelvePointTwo where b > ?" withArgumentsInArray:[NSArray arrayWithObject:[NSNumber numberWithInteger:1]]];
  290. FMDBQuickCheck([rs next]);
  291. FMDBQuickCheck([rs hasAnotherRow]);
  292. FMDBQuickCheck(![db hadError]);
  293. FMDBQuickCheck([[rs stringForColumnIndex:0] isEqualToString:@"one"]);
  294. FMDBQuickCheck([rs intForColumnIndex:1] == 2);
  295. FMDBQuickCheck([rs next]);
  296. FMDBQuickCheck([rs intForColumnIndex:1] == 3);
  297. FMDBQuickCheck(![rs next]);
  298. FMDBQuickCheck(![rs hasAnotherRow]);
  299. }
  300. {
  301. FMDBQuickCheck([db executeUpdate:@"create table t4 (a text, b text)"]);
  302. FMDBQuickCheck(([db executeUpdate:@"insert into t4 (a, b) values (?, ?)", @"one", @"two"]));
  303. rs = [db executeQuery:@"select t4.a as 't4.a', t4.b from t4;"];
  304. FMDBQuickCheck((rs != nil));
  305. [rs next];
  306. FMDBQuickCheck([[rs stringForColumn:@"t4.a"] isEqualToString:@"one"]);
  307. FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"two"]);
  308. FMDBQuickCheck(strcmp((const char*)[rs UTF8StringForColumnName:@"b"], "two") == 0);
  309. [rs close];
  310. // let's try these again, with the withArgumentsInArray: variation
  311. FMDBQuickCheck([db executeUpdate:@"drop table t4;" withArgumentsInArray:[NSArray array]]);
  312. FMDBQuickCheck([db executeUpdate:@"create table t4 (a text, b text)" withArgumentsInArray:[NSArray array]]);
  313. FMDBQuickCheck(([db executeUpdate:@"insert into t4 (a, b) values (?, ?)" withArgumentsInArray:[NSArray arrayWithObjects:@"one", @"two", nil]]));
  314. rs = [db executeQuery:@"select t4.a as 't4.a', t4.b from t4;" withArgumentsInArray:[NSArray array]];
  315. FMDBQuickCheck((rs != nil));
  316. [rs next];
  317. FMDBQuickCheck([[rs stringForColumn:@"t4.a"] isEqualToString:@"one"]);
  318. FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"two"]);
  319. FMDBQuickCheck(strcmp((const char*)[rs UTF8StringForColumnName:@"b"], "two") == 0);
  320. [rs close];
  321. }
  322. {
  323. FMDBQuickCheck([db tableExists:@"t4"]);
  324. FMDBQuickCheck(![db tableExists:@"thisdoesntexist"]);
  325. rs = [db getSchema];
  326. while ([rs next]) {
  327. FMDBQuickCheck([[rs stringForColumn:@"type"] isEqualToString:@"table"]);
  328. }
  329. }
  330. {
  331. FMDBQuickCheck([db executeUpdate:@"create table t5 (a text, b int, c blob, d text, e text)"]);
  332. FMDBQuickCheck(([db executeUpdateWithFormat:@"insert into t5 values (%s, %d, %@, %c, %lld)", "text", 42, @"BLOB", 'd', 12345678901234]));
  333. rs = [db executeQueryWithFormat:@"select * from t5 where a = %s", "text"];
  334. FMDBQuickCheck((rs != nil));
  335. [rs next];
  336. FMDBQuickCheck([[rs stringForColumn:@"a"] isEqualToString:@"text"]);
  337. FMDBQuickCheck(([rs intForColumn:@"b"] == 42));
  338. FMDBQuickCheck([[rs stringForColumn:@"c"] isEqualToString:@"BLOB"]);
  339. FMDBQuickCheck([[rs stringForColumn:@"d"] isEqualToString:@"d"]);
  340. FMDBQuickCheck(([rs longLongIntForColumn:@"e"] == 12345678901234));
  341. [rs close];
  342. }
  343. {
  344. FMDBQuickCheck([db executeUpdate:@"create table t55 (a text, b int, c float)"]);
  345. short testShort = -4;
  346. float testFloat = 5.5;
  347. FMDBQuickCheck(([db executeUpdateWithFormat:@"insert into t55 values (%c, %hi, %g)", 'a', testShort, testFloat]));
  348. unsigned short testUShort = 6;
  349. FMDBQuickCheck(([db executeUpdateWithFormat:@"insert into t55 values (%c, %hu, %g)", 'a', testUShort, testFloat]));
  350. rs = [db executeQueryWithFormat:@"select * from t55 where a = %s order by 2", "a"];
  351. FMDBQuickCheck((rs != nil));
  352. [rs next];
  353. FMDBQuickCheck([[rs stringForColumn:@"a"] isEqualToString:@"a"]);
  354. FMDBQuickCheck(([rs intForColumn:@"b"] == -4));
  355. FMDBQuickCheck([[rs stringForColumn:@"c"] isEqualToString:@"5.5"]);
  356. [rs next];
  357. FMDBQuickCheck([[rs stringForColumn:@"a"] isEqualToString:@"a"]);
  358. FMDBQuickCheck(([rs intForColumn:@"b"] == 6));
  359. FMDBQuickCheck([[rs stringForColumn:@"c"] isEqualToString:@"5.5"]);
  360. [rs close];
  361. }
  362. {
  363. NSError *err;
  364. FMDBQuickCheck(([db update:@"insert into t5 values (?, ?, ?, ?, ?)" withErrorAndBindings:&err, @"text", [NSNumber numberWithInt:42], @"BLOB", @"d", [NSNumber numberWithInt:0]]));
  365. }
  366. {
  367. // -------------------------------------------------------------------------------
  368. // Named parameters.
  369. FMDBQuickCheck([db executeUpdate:@"create table namedparamtest (a text, b text, c integer, d double)"]);
  370. NSMutableDictionary *dictionaryArgs = [NSMutableDictionary dictionary];
  371. [dictionaryArgs setObject:@"Text1" forKey:@"a"];
  372. [dictionaryArgs setObject:@"Text2" forKey:@"b"];
  373. [dictionaryArgs setObject:[NSNumber numberWithInt:1] forKey:@"c"];
  374. [dictionaryArgs setObject:[NSNumber numberWithDouble:2.0] forKey:@"d"];
  375. FMDBQuickCheck([db executeUpdate:@"insert into namedparamtest values (:a, :b, :c, :d)" withParameterDictionary:dictionaryArgs]);
  376. rs = [db executeQuery:@"select * from namedparamtest"];
  377. FMDBQuickCheck((rs != nil));
  378. [rs next];
  379. FMDBQuickCheck([[rs stringForColumn:@"a"] isEqualToString:@"Text1"]);
  380. FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"Text2"]);
  381. FMDBQuickCheck([rs intForColumn:@"c"] == 1);
  382. FMDBQuickCheck([rs doubleForColumn:@"d"] == 2.0);
  383. [rs close];
  384. dictionaryArgs = [NSMutableDictionary dictionary];
  385. [dictionaryArgs setObject:@"Text2" forKey:@"blah"];
  386. rs = [db executeQuery:@"select * from namedparamtest where b = :blah" withParameterDictionary:dictionaryArgs];
  387. FMDBQuickCheck((rs != nil));
  388. FMDBQuickCheck([rs next]);
  389. FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"Text2"]);
  390. [rs close];
  391. }
  392. // just for fun.
  393. rs = [db executeQuery:@"PRAGMA database_list"];
  394. while ([rs next]) {
  395. NSString *file = [rs stringForColumn:@"file"];
  396. NSLog(@"database_list: %@", file);
  397. }
  398. // print out some stats if we are using cached statements.
  399. if ([db shouldCacheStatements]) {
  400. NSEnumerator *e = [[db cachedStatements] objectEnumerator];;
  401. FMStatement *statement;
  402. while ((statement = [e nextObject])) {
  403. NSLog(@"%@", statement);
  404. }
  405. }
  406. [db close];
  407. FMDatabasePool *dbPool = [FMDatabasePool databasePoolWithPath:dbPath];
  408. FMDBQuickCheck([dbPool countOfOpenDatabases] == 0);
  409. db = [dbPool db];
  410. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  411. FMDBQuickCheck([db tableExists:@"t4"]);
  412. FMDBQuickCheck(![db pool]);
  413. FMDatabase *db2 = [dbPool db];
  414. FMDBQuickCheck(db2 == db);
  415. db = [dbPool db];
  416. FMDBQuickCheck(db2 != db);
  417. FMDBQuickCheck([db pool]);
  418. FMDBQuickCheck([db2 pool]);
  419. FMDBQuickCheck([dbPool countOfOpenDatabases] == 2);
  420. FMDBQuickCheck([db executeUpdate:@"create table easy (a text)"]);
  421. FMDBQuickCheck([db2 executeUpdate:@"create table easy2 (a text)"]);
  422. db = [dbPool db];
  423. FMDBQuickCheck([dbPool countOfOpenDatabases] == 2);
  424. [db pushToPool];
  425. [[dbPool db] pushToPool];
  426. [[dbPool db] pushToPool];
  427. FMDBQuickCheck([dbPool countOfOpenDatabases] == 2);
  428. [dbPool releaseAllDatabases];
  429. FMDBQuickCheck([dbPool countOfOpenDatabases] == 0);
  430. [dbPool inDatabase:^(FMDatabase *aDb) {
  431. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 0);
  432. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 1);
  433. FMDBQuickCheck([aDb tableExists:@"t4"]);
  434. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 0);
  435. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 1);
  436. FMDBQuickCheck(([aDb executeUpdate:@"insert into easy (a) values (?)", @"hi"]));
  437. // just for fun.
  438. FMResultSet *rs2 = [aDb executeQuery:@"select * from easy"];
  439. FMDBQuickCheck([rs2 next]);
  440. while ([rs2 next]) { ; } // whatevers.
  441. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  442. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 0);
  443. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 1);
  444. }];
  445. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  446. {
  447. db = [[dbPool db] popFromPool];
  448. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1]];
  449. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:2]];
  450. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:3]];
  451. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 0);
  452. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 1);
  453. [db pushToPool];
  454. }
  455. {
  456. // double pop!
  457. db = [[dbPool db] popFromPool];
  458. [db popFromPool];
  459. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1]];
  460. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 0);
  461. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 1);
  462. [db pushToPool];
  463. [db pushToPool];
  464. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 1);
  465. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 0);
  466. }
  467. [[dbPool db] pushToPool];
  468. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  469. [dbPool setMaximumNumberOfDatabasesToCreate:2];
  470. FMDatabase *adb = [dbPool db];
  471. FMDatabase *bbd = [dbPool db];
  472. FMDBQuickCheck(![dbPool db]);
  473. FMDBQuickCheck([adb tableExists:@"t4"]);
  474. FMDBQuickCheck([bbd tableExists:@"t4"]);
  475. FMDBQuickCheck([dbPool countOfOpenDatabases] == 2);
  476. [dbPool setMaximumNumberOfDatabasesToCreate:0];
  477. adb = [dbPool db];
  478. bbd = [dbPool db];
  479. FMDatabase *cbd = [dbPool db];
  480. FMDBQuickCheck([adb tableExists:@"t4"]);
  481. FMDBQuickCheck([bbd tableExists:@"t4"]);
  482. FMDBQuickCheck([cbd tableExists:@"t4"]);
  483. [dbPool releaseAllDatabases];
  484. FMDBQuickCheck([dbPool countOfOpenDatabases] == 0);
  485. {
  486. db = [dbPool db];
  487. [db beginTransaction];
  488. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1]];
  489. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:2]];
  490. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:3]];
  491. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 0);
  492. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 1);
  493. [db popFromPool];
  494. [db commit];
  495. [db pushToPool];
  496. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 1);
  497. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 0);
  498. }
  499. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:3]];
  500. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  501. [dbPool inTransaction:^(FMDatabase *adb, BOOL *rollback) {
  502. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1001]];
  503. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1002]];
  504. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1003]];
  505. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  506. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 0);
  507. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 1);
  508. }];
  509. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  510. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 1);
  511. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 0);
  512. FMResultSet *rs2 = [[dbPool db] executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1001]];
  513. FMDBQuickCheck([rs2 next]);
  514. FMDBQuickCheck(![rs2 next]);
  515. [dbPool inDeferredTransaction:^(FMDatabase *adb, BOOL *rollback) {
  516. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1004]];
  517. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1005]];
  518. *rollback = YES;
  519. }];
  520. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  521. FMDBQuickCheck([dbPool countOfCheckedInDatabases] == 1);
  522. FMDBQuickCheck([dbPool countOfCheckedOutDatabases] == 0);
  523. rs2 = [[dbPool db] executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1004]];
  524. FMDBQuickCheck(![rs2 next]);
  525. rs2 = [[dbPool db] executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1005]];
  526. FMDBQuickCheck(![rs2 next]);
  527. FMDBQuickCheck([dbPool countOfOpenDatabases] == 1);
  528. err = [dbPool inSavePoint:^(FMDatabase *db, BOOL *rollback) {
  529. FMDBQuickCheck(![adb hadError]);
  530. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1006]];
  531. }];
  532. rs2 = [[dbPool db] executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1006]];
  533. FMDBQuickCheck([rs2 next]);
  534. [rs2 close];
  535. {
  536. db = [dbPool db];
  537. FMDBQuickCheck([db startSavePointWithName:@"a" error:nil]);
  538. [db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1007]];
  539. FMDBQuickCheck([db startSavePointWithName:@"b" error:nil]);
  540. FMDBQuickCheck(([db executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1008]]));
  541. FMDBQuickCheck([db releaseSavePointWithName:@"b" error:nil]);
  542. FMDBQuickCheck([db releaseSavePointWithName:@"a" error:nil]);
  543. rs2 = [[dbPool db] executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1007]];
  544. FMDBQuickCheck([rs2 next]);
  545. FMDBQuickCheck(![rs2 next]); // close it out.
  546. rs2 = [[dbPool db] executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1008]];
  547. FMDBQuickCheck([rs2 next]);
  548. FMDBQuickCheck(![rs2 next]); // close it out.
  549. }
  550. {
  551. err = [dbPool inSavePoint:^(FMDatabase *adb, BOOL *rollback) {
  552. FMDBQuickCheck(![adb hadError]);
  553. [adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1009]];
  554. [adb inSavePoint:^(BOOL *rollback) {
  555. FMDBQuickCheck(([adb executeUpdate:@"insert into easy values (?)", [NSNumber numberWithInt:1010]]));
  556. *rollback = YES;
  557. }];
  558. }];
  559. rs2 = [[dbPool db] executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1009]];
  560. FMDBQuickCheck([rs2 next]);
  561. FMDBQuickCheck(![rs2 next]); // close it out.
  562. rs2 = [[dbPool db] executeQuery:@"select * from easy where a = ?", [NSNumber numberWithInt:1010]];
  563. FMDBQuickCheck(![rs2 next]);
  564. }
  565. {
  566. [dbPool inDatabase:^(FMDatabase *db) {
  567. [db executeUpdate:@"create table likefoo (foo text)"];
  568. [db executeUpdate:@"insert into likefoo values ('hi')"];
  569. [db executeUpdate:@"insert into likefoo values ('hello')"];
  570. [db executeUpdate:@"insert into likefoo values ('not')"];
  571. int count = 0;
  572. FMResultSet *rsl = [db executeQuery:@"select * from likefoo where foo like 'h%'"];
  573. while ([rsl next]) {
  574. count++;
  575. }
  576. FMDBQuickCheck(count == 2);
  577. count = 0;
  578. rsl = [db executeQuery:@"select * from likefoo where foo like ?", @"h%"];
  579. while ([rsl next]) {
  580. count++;
  581. }
  582. FMDBQuickCheck(count == 2);
  583. }];
  584. }
  585. FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
  586. FMDBQuickCheck(queue);
  587. {
  588. [queue inDatabase:^(FMDatabase *db) {
  589. int count = 0;
  590. FMResultSet *rsl = [db executeQuery:@"select * from likefoo where foo like 'h%'"];
  591. while ([rsl next]) {
  592. count++;
  593. }
  594. FMDBQuickCheck(count == 2);
  595. count = 0;
  596. rsl = [db executeQuery:@"select * from likefoo where foo like ?", @"h%"];
  597. while ([rsl next]) {
  598. count++;
  599. }
  600. FMDBQuickCheck(count == 2);
  601. }];
  602. }
  603. {
  604. int ops = 16;
  605. dispatch_queue_t dqueue = dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_HIGH);
  606. dispatch_apply(ops, dqueue, ^(size_t nby) {
  607. // just mix things up a bit for demonstration purposes.
  608. if (nby % 2 == 1) {
  609. [NSThread sleepForTimeInterval:.1];
  610. [queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
  611. NSLog(@"Starting query %ld", nby);
  612. FMResultSet *rsl = [db executeQuery:@"select * from likefoo where foo like 'h%'"];
  613. while ([rsl next]) {
  614. ;// whatever.
  615. }
  616. NSLog(@"Ending query %ld", nby);
  617. }];
  618. }
  619. if (nby % 3 == 1) {
  620. [NSThread sleepForTimeInterval:.1];
  621. }
  622. [queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
  623. NSLog(@"Starting update %ld", nby);
  624. [db executeUpdate:@"insert into likefoo values ('1')"];
  625. [db executeUpdate:@"insert into likefoo values ('2')"];
  626. [db executeUpdate:@"insert into likefoo values ('3')"];
  627. NSLog(@"Ending update %ld", nby);
  628. }];
  629. });
  630. }
  631. NSLog(@"That was version %@ of sqlite", [FMDatabase sqliteLibVersion]);
  632. [pool release];
  633. return 0;
  634. }