FMDatabase.m 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207
  1. #import "FMDatabase.h"
  2. #import "unistd.h"
  3. #import <objc/runtime.h>
  4. @interface FMDatabase ()
  5. - (void)checkPoolPushBack;
  6. - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args;
  7. - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args;
  8. @end
  9. @implementation FMDatabase
  10. @synthesize cachedStatements=_cachedStatements;
  11. @synthesize logsErrors=_logsErrors;
  12. @synthesize crashOnErrors=_crashOnErrors;
  13. @synthesize busyRetryTimeout=_busyRetryTimeout;
  14. @synthesize checkedOut=_checkedOut;
  15. @synthesize traceExecution=_traceExecution;
  16. + (id)databaseWithPath:(NSString*)aPath {
  17. return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]);
  18. }
  19. + (NSString*)sqliteLibVersion {
  20. return [NSString stringWithFormat:@"%s", sqlite3_libversion()];
  21. }
  22. + (BOOL)isSQLiteThreadSafe {
  23. // make sure to read the sqlite headers on this guy!
  24. return sqlite3_threadsafe();
  25. }
  26. - (id)initWithPath:(NSString*)aPath {
  27. assert(sqlite3_threadsafe()); // whoa there big boy- gotta make sure sqlite it happy with what we're going to do.
  28. self = [super init];
  29. if (self) {
  30. _databasePath = [aPath copy];
  31. _openResultSets = [[NSMutableSet alloc] init];
  32. _db = 0x00;
  33. _logsErrors = 0x00;
  34. _crashOnErrors = 0x00;
  35. _busyRetryTimeout = 0x00;
  36. }
  37. return self;
  38. }
  39. - (void)finalize {
  40. [self close];
  41. [super finalize];
  42. }
  43. - (void)dealloc {
  44. [self close];
  45. FMDBRelease(_openResultSets);
  46. FMDBRelease(_cachedStatements);
  47. FMDBRelease(_databasePath);
  48. FMDBRelease(_openFunctions);
  49. [self setPool:0x00];
  50. #if ! __has_feature(objc_arc)
  51. [super dealloc];
  52. #endif
  53. }
  54. - (NSString *)databasePath {
  55. return _databasePath;
  56. }
  57. - (sqlite3*)sqliteHandle {
  58. return _db;
  59. }
  60. - (BOOL)open {
  61. if (_db) {
  62. return YES;
  63. }
  64. int err = sqlite3_open((_databasePath ? [_databasePath fileSystemRepresentation] : ":memory:"), &_db );
  65. if(err != SQLITE_OK) {
  66. NSLog(@"error opening!: %d", err);
  67. return NO;
  68. }
  69. return YES;
  70. }
  71. #if SQLITE_VERSION_NUMBER >= 3005000
  72. - (BOOL)openWithFlags:(int)flags {
  73. int err = sqlite3_open_v2((_databasePath ? [_databasePath fileSystemRepresentation] : ":memory:"), &_db, flags, NULL /* Name of VFS module to use */);
  74. if(err != SQLITE_OK) {
  75. NSLog(@"error opening!: %d", err);
  76. return NO;
  77. }
  78. return YES;
  79. }
  80. #endif
  81. - (BOOL)close {
  82. [self clearCachedStatements];
  83. [self closeOpenResultSets];
  84. if (!_db) {
  85. return YES;
  86. }
  87. int rc;
  88. BOOL retry;
  89. int numberOfRetries = 0;
  90. BOOL triedFinalizingOpenStatements = NO;
  91. do {
  92. retry = NO;
  93. rc = sqlite3_close(_db);
  94. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  95. retry = YES;
  96. usleep(20);
  97. if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) {
  98. NSLog(@"%s:%d", __FUNCTION__, __LINE__);
  99. NSLog(@"Database busy, unable to close");
  100. return NO;
  101. }
  102. if (!triedFinalizingOpenStatements) {
  103. triedFinalizingOpenStatements = YES;
  104. sqlite3_stmt *pStmt;
  105. while ((pStmt = sqlite3_next_stmt(_db, 0x00)) !=0) {
  106. NSLog(@"Closing leaked statement");
  107. sqlite3_finalize(pStmt);
  108. }
  109. }
  110. }
  111. else if (SQLITE_OK != rc) {
  112. NSLog(@"error closing!: %d", rc);
  113. }
  114. }
  115. while (retry);
  116. _db = nil;
  117. return YES;
  118. }
  119. - (void)clearCachedStatements {
  120. for (FMStatement *cachedStmt in [_cachedStatements objectEnumerator]) {
  121. //NSLog(@"cachedStmt: '%@'", cachedStmt);
  122. [cachedStmt close];
  123. }
  124. [_cachedStatements removeAllObjects];
  125. }
  126. - (BOOL)hasOpenResultSets {
  127. return [_openResultSets count] > 0;
  128. }
  129. - (void)closeOpenResultSets {
  130. //Copy the set so we don't get mutation errors
  131. NSMutableSet *openSetCopy = FMDBReturnAutoreleased([_openResultSets copy]);
  132. for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) {
  133. FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue];
  134. [rs setParentDB:nil];
  135. [rs close];
  136. [_openResultSets removeObject:rsInWrappedInATastyValueMeal];
  137. }
  138. }
  139. - (void)resultSetDidClose:(FMResultSet *)resultSet {
  140. NSValue *setValue = [NSValue valueWithNonretainedObject:resultSet];
  141. [_openResultSets removeObject:setValue];
  142. [self checkPoolPushBack];
  143. }
  144. - (FMStatement*)cachedStatementForQuery:(NSString*)query {
  145. return [_cachedStatements objectForKey:query];
  146. }
  147. - (void)setCachedStatement:(FMStatement*)statement forQuery:(NSString*)query {
  148. //NSLog(@"setting query: %@", query);
  149. query = [query copy]; // in case we got handed in a mutable string...
  150. [statement setQuery:query];
  151. [_cachedStatements setObject:statement forKey:query];
  152. FMDBRelease(query);
  153. }
  154. - (BOOL)rekey:(NSString*)key {
  155. #ifdef SQLITE_HAS_CODEC
  156. if (!key) {
  157. return NO;
  158. }
  159. int rc = sqlite3_rekey(_db, [key UTF8String], (int)strlen([key UTF8String]));
  160. if (rc != SQLITE_OK) {
  161. NSLog(@"error on rekey: %d", rc);
  162. NSLog(@"%@", [self lastErrorMessage]);
  163. }
  164. return (rc == SQLITE_OK);
  165. #else
  166. return NO;
  167. #endif
  168. }
  169. - (BOOL)setKey:(NSString*)key {
  170. #ifdef SQLITE_HAS_CODEC
  171. if (!key) {
  172. return NO;
  173. }
  174. int rc = sqlite3_key(_db, [key UTF8String], (int)strlen([key UTF8String]));
  175. return (rc == SQLITE_OK);
  176. #else
  177. return NO;
  178. #endif
  179. }
  180. - (BOOL)goodConnection {
  181. if (!_db) {
  182. return NO;
  183. }
  184. FMResultSet *rs = [self executeQuery:@"select name from sqlite_master where type='table'"];
  185. if (rs) {
  186. [rs close];
  187. return YES;
  188. }
  189. return NO;
  190. }
  191. - (void)warnInUse {
  192. NSLog(@"The FMDatabase %@ is currently in use.", self);
  193. #ifndef NS_BLOCK_ASSERTIONS
  194. if (_crashOnErrors) {
  195. abort();
  196. NSAssert1(false, @"The FMDatabase %@ is currently in use.", self);
  197. }
  198. #endif
  199. }
  200. - (BOOL)databaseExists {
  201. if (!_db) {
  202. NSLog(@"The FMDatabase %@ is not open.", self);
  203. #ifndef NS_BLOCK_ASSERTIONS
  204. if (_crashOnErrors) {
  205. abort();
  206. NSAssert1(false, @"The FMDatabase %@ is not open.", self);
  207. }
  208. #endif
  209. return NO;
  210. }
  211. return YES;
  212. }
  213. - (NSString*)lastErrorMessage {
  214. return [NSString stringWithUTF8String:sqlite3_errmsg(_db)];
  215. }
  216. - (BOOL)hadError {
  217. int lastErrCode = [self lastErrorCode];
  218. return (lastErrCode > SQLITE_OK && lastErrCode < SQLITE_ROW);
  219. }
  220. - (int)lastErrorCode {
  221. return sqlite3_errcode(_db);
  222. }
  223. - (NSError*)lastError {
  224. return [NSError errorWithDomain:@"FMDatabase" code:sqlite3_errcode(_db) userInfo:[NSDictionary dictionaryWithObject:[self lastErrorMessage] forKey:NSLocalizedDescriptionKey]];
  225. }
  226. - (sqlite_int64)lastInsertRowId {
  227. if (_isExecutingStatement) {
  228. [self warnInUse];
  229. return NO;
  230. }
  231. _isExecutingStatement = YES;
  232. sqlite_int64 ret = sqlite3_last_insert_rowid(_db);
  233. _isExecutingStatement = NO;
  234. return ret;
  235. }
  236. - (int)changes {
  237. if (_isExecutingStatement) {
  238. [self warnInUse];
  239. return 0;
  240. }
  241. _isExecutingStatement = YES;
  242. int ret = sqlite3_changes(_db);
  243. _isExecutingStatement = NO;
  244. return ret;
  245. }
  246. - (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt {
  247. if ((!obj) || ((NSNull *)obj == [NSNull null])) {
  248. sqlite3_bind_null(pStmt, idx);
  249. }
  250. // FIXME - someday check the return codes on these binds.
  251. else if ([obj isKindOfClass:[NSData class]]) {
  252. sqlite3_bind_blob(pStmt, idx, [obj bytes], (int)[obj length], SQLITE_STATIC);
  253. }
  254. else if ([obj isKindOfClass:[NSDate class]]) {
  255. sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]);
  256. }
  257. else if ([obj isKindOfClass:[NSNumber class]]) {
  258. if (strcmp([obj objCType], @encode(BOOL)) == 0) {
  259. sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0));
  260. }
  261. else if (strcmp([obj objCType], @encode(int)) == 0) {
  262. sqlite3_bind_int64(pStmt, idx, [obj longValue]);
  263. }
  264. else if (strcmp([obj objCType], @encode(long)) == 0) {
  265. sqlite3_bind_int64(pStmt, idx, [obj longValue]);
  266. }
  267. else if (strcmp([obj objCType], @encode(long long)) == 0) {
  268. sqlite3_bind_int64(pStmt, idx, [obj longLongValue]);
  269. }
  270. else if (strcmp([obj objCType], @encode(float)) == 0) {
  271. sqlite3_bind_double(pStmt, idx, [obj floatValue]);
  272. }
  273. else if (strcmp([obj objCType], @encode(double)) == 0) {
  274. sqlite3_bind_double(pStmt, idx, [obj doubleValue]);
  275. }
  276. else {
  277. sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
  278. }
  279. }
  280. else {
  281. sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
  282. }
  283. }
  284. - (void)extractSQL:(NSString *)sql argumentsList:(va_list)args intoString:(NSMutableString *)cleanedSQL arguments:(NSMutableArray *)arguments {
  285. NSUInteger length = [sql length];
  286. unichar last = '\0';
  287. for (NSUInteger i = 0; i < length; ++i) {
  288. id arg = nil;
  289. unichar current = [sql characterAtIndex:i];
  290. unichar add = current;
  291. if (last == '%') {
  292. switch (current) {
  293. case '@':
  294. arg = va_arg(args, id); break;
  295. case 'c':
  296. // warning: second argument to 'va_arg' is of promotable type 'char'; this va_arg has undefined behavior because arguments will be promoted to 'int'
  297. arg = [NSString stringWithFormat:@"%c", va_arg(args, int)]; break;
  298. case 's':
  299. arg = [NSString stringWithUTF8String:va_arg(args, char*)]; break;
  300. case 'd':
  301. case 'D':
  302. case 'i':
  303. arg = [NSNumber numberWithInt:va_arg(args, int)]; break;
  304. case 'u':
  305. case 'U':
  306. arg = [NSNumber numberWithUnsignedInt:va_arg(args, unsigned int)]; break;
  307. case 'h':
  308. i++;
  309. if (i < length && [sql characterAtIndex:i] == 'i') {
  310. // warning: second argument to 'va_arg' is of promotable type 'short'; this va_arg has undefined behavior because arguments will be promoted to 'int'
  311. arg = [NSNumber numberWithShort:va_arg(args, int)];
  312. }
  313. else if (i < length && [sql characterAtIndex:i] == 'u') {
  314. // warning: second argument to 'va_arg' is of promotable type 'unsigned short'; this va_arg has undefined behavior because arguments will be promoted to 'int'
  315. arg = [NSNumber numberWithUnsignedShort:va_arg(args, uint)];
  316. }
  317. else {
  318. i--;
  319. }
  320. break;
  321. case 'q':
  322. i++;
  323. if (i < length && [sql characterAtIndex:i] == 'i') {
  324. arg = [NSNumber numberWithLongLong:va_arg(args, long long)];
  325. }
  326. else if (i < length && [sql characterAtIndex:i] == 'u') {
  327. arg = [NSNumber numberWithUnsignedLongLong:va_arg(args, unsigned long long)];
  328. }
  329. else {
  330. i--;
  331. }
  332. break;
  333. case 'f':
  334. arg = [NSNumber numberWithDouble:va_arg(args, double)]; break;
  335. case 'g':
  336. // warning: second argument to 'va_arg' is of promotable type 'float'; this va_arg has undefined behavior because arguments will be promoted to 'double'
  337. arg = [NSNumber numberWithFloat:va_arg(args, double)]; break;
  338. case 'l':
  339. i++;
  340. if (i < length) {
  341. unichar next = [sql characterAtIndex:i];
  342. if (next == 'l') {
  343. i++;
  344. if (i < length && [sql characterAtIndex:i] == 'd') {
  345. //%lld
  346. arg = [NSNumber numberWithLongLong:va_arg(args, long long)];
  347. }
  348. else if (i < length && [sql characterAtIndex:i] == 'u') {
  349. //%llu
  350. arg = [NSNumber numberWithUnsignedLongLong:va_arg(args, unsigned long long)];
  351. }
  352. else {
  353. i--;
  354. }
  355. }
  356. else if (next == 'd') {
  357. //%ld
  358. arg = [NSNumber numberWithLong:va_arg(args, long)];
  359. }
  360. else if (next == 'u') {
  361. //%lu
  362. arg = [NSNumber numberWithUnsignedLong:va_arg(args, unsigned long)];
  363. }
  364. else {
  365. i--;
  366. }
  367. }
  368. else {
  369. i--;
  370. }
  371. break;
  372. default:
  373. // something else that we can't interpret. just pass it on through like normal
  374. break;
  375. }
  376. }
  377. else if (current == '%') {
  378. // percent sign; skip this character
  379. add = '\0';
  380. }
  381. if (arg != nil) {
  382. [cleanedSQL appendString:@"?"];
  383. [arguments addObject:arg];
  384. }
  385. else if (add != '\0') {
  386. [cleanedSQL appendFormat:@"%C", add];
  387. }
  388. last = current;
  389. }
  390. }
  391. - (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments {
  392. return [self executeQuery:sql withArgumentsInArray:nil orDictionary:arguments orVAList:nil];
  393. }
  394. - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
  395. if (![self databaseExists]) {
  396. //Pushing the FMDatabase instance back to the pool if error occurs
  397. [self checkPoolPushBack];
  398. return 0x00;
  399. }
  400. if (_isExecutingStatement) {
  401. [self warnInUse];
  402. [self checkPoolPushBack];
  403. return 0x00;
  404. }
  405. _isExecutingStatement = YES;
  406. int rc = 0x00;
  407. sqlite3_stmt *pStmt = 0x00;
  408. FMStatement *statement = 0x00;
  409. FMResultSet *rs = 0x00;
  410. if (_traceExecution && sql) {
  411. NSLog(@"%@ executeQuery: %@", self, sql);
  412. }
  413. if (_shouldCacheStatements) {
  414. statement = [self cachedStatementForQuery:sql];
  415. pStmt = statement ? [statement statement] : 0x00;
  416. }
  417. int numberOfRetries = 0;
  418. BOOL retry = NO;
  419. if (!pStmt) {
  420. do {
  421. retry = NO;
  422. rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
  423. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  424. retry = YES;
  425. usleep(20);
  426. if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) {
  427. NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
  428. NSLog(@"Database busy");
  429. sqlite3_finalize(pStmt);
  430. _isExecutingStatement = NO;
  431. [self checkPoolPushBack];
  432. return nil;
  433. }
  434. }
  435. else if (SQLITE_OK != rc) {
  436. if (_logsErrors) {
  437. NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  438. NSLog(@"DB Query: %@", sql);
  439. NSLog(@"DB Path: %@", _databasePath);
  440. #ifndef NS_BLOCK_ASSERTIONS
  441. if (_crashOnErrors) {
  442. abort();
  443. NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  444. }
  445. #endif
  446. }
  447. sqlite3_finalize(pStmt);
  448. _isExecutingStatement = NO;
  449. [self checkPoolPushBack];
  450. return nil;
  451. }
  452. }
  453. while (retry);
  454. }
  455. id obj;
  456. int idx = 0;
  457. int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!)
  458. // If dictionaryArgs is passed in, that means we are using sqlite's named parameter support
  459. if (dictionaryArgs) {
  460. for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
  461. // Prefix the key with a colon.
  462. NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
  463. // Get the index for the parameter name.
  464. int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
  465. FMDBRelease(parameterName);
  466. if (namedIdx > 0) {
  467. // Standard binding from here.
  468. [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
  469. }
  470. else {
  471. NSLog(@"Could not find index for %@", dictionaryKey);
  472. }
  473. }
  474. // we need the count of params to avoid an error below.
  475. idx = (int) [[dictionaryArgs allKeys] count];
  476. }
  477. else {
  478. while (idx < queryCount) {
  479. if (arrayArgs) {
  480. obj = [arrayArgs objectAtIndex:idx];
  481. }
  482. else {
  483. obj = va_arg(args, id);
  484. }
  485. if (_traceExecution) {
  486. NSLog(@"obj: %@", obj);
  487. }
  488. idx++;
  489. [self bindObject:obj toColumn:idx inStatement:pStmt];
  490. }
  491. }
  492. if (idx != queryCount) {
  493. NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)");
  494. sqlite3_finalize(pStmt);
  495. _isExecutingStatement = NO;
  496. [self checkPoolPushBack];
  497. return nil;
  498. }
  499. FMDBRetain(statement); // to balance the release below
  500. if (!statement) {
  501. statement = [[FMStatement alloc] init];
  502. [statement setStatement:pStmt];
  503. if (_shouldCacheStatements) {
  504. [self setCachedStatement:statement forQuery:sql];
  505. }
  506. }
  507. // the statement gets closed in rs's dealloc or [rs close];
  508. rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self];
  509. [rs setQuery:sql];
  510. NSValue *openResultSet = [NSValue valueWithNonretainedObject:rs];
  511. [_openResultSets addObject:openResultSet];
  512. [statement setUseCount:[statement useCount] + 1];
  513. FMDBRelease(statement);
  514. _isExecutingStatement = NO;
  515. return rs;
  516. }
  517. - (FMResultSet *)executeQuery:(NSString*)sql, ... {
  518. va_list args;
  519. va_start(args, sql);
  520. id result = [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args];
  521. va_end(args);
  522. return result;
  523. }
  524. - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... {
  525. va_list args;
  526. va_start(args, format);
  527. NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]];
  528. NSMutableArray *arguments = [NSMutableArray array];
  529. [self extractSQL:format argumentsList:args intoString:sql arguments:arguments];
  530. va_end(args);
  531. return [self executeQuery:sql withArgumentsInArray:arguments];
  532. }
  533. - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments {
  534. return [self executeQuery:sql withArgumentsInArray:arguments orDictionary:nil orVAList:nil];
  535. }
  536. - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
  537. if (![self databaseExists]) {
  538. [self checkPoolPushBack];
  539. return NO;
  540. }
  541. if (_isExecutingStatement) {
  542. [self checkPoolPushBack];
  543. [self warnInUse];
  544. return NO;
  545. }
  546. _isExecutingStatement = YES;
  547. int rc = 0x00;
  548. sqlite3_stmt *pStmt = 0x00;
  549. FMStatement *cachedStmt = 0x00;
  550. if (_traceExecution && sql) {
  551. NSLog(@"%@ executeUpdate: %@", self, sql);
  552. }
  553. if (_shouldCacheStatements) {
  554. cachedStmt = [self cachedStatementForQuery:sql];
  555. pStmt = cachedStmt ? [cachedStmt statement] : 0x00;
  556. }
  557. int numberOfRetries = 0;
  558. BOOL retry = NO;
  559. if (!pStmt) {
  560. do {
  561. retry = NO;
  562. rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
  563. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  564. retry = YES;
  565. usleep(20);
  566. if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) {
  567. NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
  568. NSLog(@"Database busy");
  569. sqlite3_finalize(pStmt);
  570. _isExecutingStatement = NO;
  571. [self checkPoolPushBack];
  572. return NO;
  573. }
  574. }
  575. else if (SQLITE_OK != rc) {
  576. if (_logsErrors) {
  577. NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  578. NSLog(@"DB Query: %@", sql);
  579. NSLog(@"DB Path: %@", _databasePath);
  580. #ifndef NS_BLOCK_ASSERTIONS
  581. if (_crashOnErrors) {
  582. abort();
  583. NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  584. }
  585. #endif
  586. }
  587. sqlite3_finalize(pStmt);
  588. if (outErr) {
  589. *outErr = [NSError errorWithDomain:[NSString stringWithUTF8String:sqlite3_errmsg(_db)] code:rc userInfo:nil];
  590. }
  591. _isExecutingStatement = NO;
  592. [self checkPoolPushBack];
  593. return NO;
  594. }
  595. }
  596. while (retry);
  597. }
  598. id obj;
  599. int idx = 0;
  600. int queryCount = sqlite3_bind_parameter_count(pStmt);
  601. // If dictionaryArgs is passed in, that means we are using sqlite's named parameter support
  602. if (dictionaryArgs) {
  603. for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
  604. // Prefix the key with a colon.
  605. NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
  606. // Get the index for the parameter name.
  607. int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
  608. FMDBRelease(parameterName);
  609. if (namedIdx > 0) {
  610. // Standard binding from here.
  611. [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
  612. }
  613. else {
  614. NSLog(@"Could not find index for %@", dictionaryKey);
  615. }
  616. }
  617. // we need the count of params to avoid an error below.
  618. idx = (int) [[dictionaryArgs allKeys] count];
  619. }
  620. else {
  621. while (idx < queryCount) {
  622. if (arrayArgs) {
  623. obj = [arrayArgs objectAtIndex:idx];
  624. }
  625. else {
  626. obj = va_arg(args, id);
  627. }
  628. if (_traceExecution) {
  629. NSLog(@"obj: %@", obj);
  630. }
  631. idx++;
  632. [self bindObject:obj toColumn:idx inStatement:pStmt];
  633. }
  634. }
  635. if (idx != queryCount) {
  636. NSLog(@"Error: the bind count is not correct for the # of variables (%@) (executeUpdate)", sql);
  637. sqlite3_finalize(pStmt);
  638. _isExecutingStatement = NO;
  639. [self checkPoolPushBack];
  640. return NO;
  641. }
  642. /* Call sqlite3_step() to run the virtual machine. Since the SQL being
  643. ** executed is not a SELECT statement, we assume no data will be returned.
  644. */
  645. numberOfRetries = 0;
  646. do {
  647. rc = sqlite3_step(pStmt);
  648. retry = NO;
  649. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  650. // this will happen if the db is locked, like if we are doing an update or insert.
  651. // in that case, retry the step... and maybe wait just 10 milliseconds.
  652. retry = YES;
  653. if (SQLITE_LOCKED == rc) {
  654. rc = sqlite3_reset(pStmt);
  655. if (rc != SQLITE_LOCKED) {
  656. NSLog(@"Unexpected result from sqlite3_reset (%d) eu", rc);
  657. }
  658. }
  659. usleep(20);
  660. if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) {
  661. NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
  662. NSLog(@"Database busy");
  663. retry = NO;
  664. }
  665. }
  666. else if (SQLITE_DONE == rc) {
  667. // all is well, let's return.
  668. }
  669. else if (SQLITE_ERROR == rc) {
  670. NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db));
  671. NSLog(@"DB Query: %@", sql);
  672. }
  673. else if (SQLITE_MISUSE == rc) {
  674. // uh oh.
  675. NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db));
  676. NSLog(@"DB Query: %@", sql);
  677. }
  678. else {
  679. // wtf?
  680. NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db));
  681. NSLog(@"DB Query: %@", sql);
  682. }
  683. } while (retry);
  684. if (rc == SQLITE_ROW) {
  685. NSAssert1(NO, @"A executeUpdate is being called with a query string '%@'", sql);
  686. }
  687. if (_shouldCacheStatements && !cachedStmt) {
  688. cachedStmt = [[FMStatement alloc] init];
  689. [cachedStmt setStatement:pStmt];
  690. [self setCachedStatement:cachedStmt forQuery:sql];
  691. FMDBRelease(cachedStmt);
  692. }
  693. int closeErrorCode;
  694. if (cachedStmt) {
  695. [cachedStmt setUseCount:[cachedStmt useCount] + 1];
  696. closeErrorCode = sqlite3_reset(pStmt);
  697. }
  698. else {
  699. /* Finalize the virtual machine. This releases all memory and other
  700. ** resources allocated by the sqlite3_prepare() call above.
  701. */
  702. closeErrorCode = sqlite3_finalize(pStmt);
  703. }
  704. if (closeErrorCode != SQLITE_OK) {
  705. NSLog(@"Unknown error finalizing or resetting statement (%d: %s)", closeErrorCode, sqlite3_errmsg(_db));
  706. NSLog(@"DB Query: %@", sql);
  707. }
  708. _isExecutingStatement = NO;
  709. [self checkPoolPushBack];
  710. return (rc == SQLITE_DONE || rc == SQLITE_OK);
  711. }
  712. - (BOOL)executeUpdate:(NSString*)sql, ... {
  713. va_list args;
  714. va_start(args, sql);
  715. BOOL result = [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:nil orVAList:args];
  716. va_end(args);
  717. return result;
  718. }
  719. - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments {
  720. return [self executeUpdate:sql error:nil withArgumentsInArray:arguments orDictionary:nil orVAList:nil];
  721. }
  722. - (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments {
  723. return [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:arguments orVAList:nil];
  724. }
  725. - (BOOL)executeUpdateWithFormat:(NSString*)format, ... {
  726. va_list args;
  727. va_start(args, format);
  728. NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]];
  729. NSMutableArray *arguments = [NSMutableArray array];
  730. [self extractSQL:format argumentsList:args intoString:sql arguments:arguments];
  731. va_end(args);
  732. return [self executeUpdate:sql withArgumentsInArray:arguments];
  733. }
  734. - (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ... {
  735. va_list args;
  736. va_start(args, outErr);
  737. BOOL result = [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:args];
  738. va_end(args);
  739. return result;
  740. }
  741. - (BOOL)rollback {
  742. BOOL b = [self executeUpdate:@"rollback transaction"];
  743. [self pushToPool];
  744. if (b) {
  745. _inTransaction = NO;
  746. }
  747. return b;
  748. }
  749. - (BOOL)commit {
  750. BOOL b = [self executeUpdate:@"commit transaction"];
  751. [self pushToPool];
  752. if (b) {
  753. _inTransaction = NO;
  754. }
  755. return b;
  756. }
  757. - (BOOL)beginDeferredTransaction {
  758. if ([self pool]) {
  759. [self popFromPool];
  760. }
  761. BOOL b = [self executeUpdate:@"begin deferred transaction"];
  762. if (b) {
  763. _inTransaction = YES;
  764. }
  765. return b;
  766. }
  767. - (BOOL)beginTransaction {
  768. if ([self pool]) {
  769. [self popFromPool];
  770. }
  771. BOOL b = [self executeUpdate:@"begin exclusive transaction"];
  772. if (b) {
  773. _inTransaction = YES;
  774. }
  775. return b;
  776. }
  777. - (BOOL)inTransaction {
  778. return _inTransaction;
  779. }
  780. #if SQLITE_VERSION_NUMBER >= 3007000
  781. - (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr {
  782. // FIXME: make sure the savepoint name doesn't have a ' in it.
  783. NSAssert1(name, @"Missing name for a savepoint", nil);
  784. if ([self pool]) {
  785. [self popFromPool];
  786. }
  787. if (![self executeUpdate:[NSString stringWithFormat:@"savepoint '%@';", name]]) {
  788. if (*outErr) {
  789. *outErr = [self lastError];
  790. }
  791. return NO;
  792. }
  793. return YES;
  794. }
  795. - (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr {
  796. NSAssert1(name, @"Missing name for a savepoint", nil);
  797. BOOL worked = [self executeUpdate:[NSString stringWithFormat:@"release savepoint '%@';", name]];
  798. if (!worked && *outErr) {
  799. *outErr = [self lastError];
  800. }
  801. if ([self pool]) {
  802. [self pushToPool];
  803. }
  804. return worked;
  805. }
  806. - (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr {
  807. NSAssert1(name, @"Missing name for a savepoint", nil);
  808. BOOL worked = [self executeUpdate:[NSString stringWithFormat:@"rollback transaction to savepoint '%@';", name]];
  809. if (!worked && *outErr) {
  810. *outErr = [self lastError];
  811. }
  812. if ([self pool]) {
  813. [self pushToPool];
  814. }
  815. return worked;
  816. }
  817. - (NSError*)inSavePoint:(void (^)(BOOL *rollback))block {
  818. static unsigned long savePointIdx = 0;
  819. NSString *name = [NSString stringWithFormat:@"dbSavePoint%ld", savePointIdx++];
  820. BOOL shouldRollback = NO;
  821. NSError *err = 0x00;
  822. if (![self startSavePointWithName:name error:&err]) {
  823. return err;
  824. }
  825. block(&shouldRollback);
  826. if (shouldRollback) {
  827. [self rollbackToSavePointWithName:name error:&err];
  828. }
  829. else {
  830. [self releaseSavePointWithName:name error:&err];
  831. }
  832. return err;
  833. }
  834. #endif
  835. - (BOOL)shouldCacheStatements {
  836. return _shouldCacheStatements;
  837. }
  838. - (void)setShouldCacheStatements:(BOOL)value {
  839. _shouldCacheStatements = value;
  840. if (_shouldCacheStatements && !_cachedStatements) {
  841. [self setCachedStatements:[NSMutableDictionary dictionary]];
  842. }
  843. if (!_shouldCacheStatements) {
  844. [self setCachedStatements:nil];
  845. }
  846. }
  847. - (FMDatabase*)popFromPool {
  848. if (![self pool]) {
  849. NSLog(@"No FMDatabasePool in place for %@", self);
  850. return 0x00;
  851. }
  852. _poolPopCount++;
  853. return self;
  854. }
  855. - (void)pushToPool {
  856. _poolPopCount--;
  857. [self checkPoolPushBack];
  858. }
  859. - (void)checkPoolPushBack {
  860. if (_poolPopCount <= 0) {
  861. [[self pool] pushDatabaseBackInPool:self];
  862. if (_poolPopCount < 0) {
  863. _poolPopCount = 0;
  864. }
  865. }
  866. }
  867. // #if __has_feature(objc_arc_weak)
  868. - (FMDatabasePool *)pool {
  869. #if FMDB_USE_WEAK_POOL && (!__has_feature(objc_arc_weak))
  870. return objc_loadWeak(&_poolAccessViaMethodOnly);
  871. #else
  872. return _poolAccessViaMethodOnly;
  873. #endif
  874. }
  875. - (void)setPool:(FMDatabasePool *)value {
  876. #if FMDB_USE_WEAK_POOL && (!__has_feature(objc_arc_weak))
  877. objc_storeWeak(&_poolAccessViaMethodOnly, value);
  878. #else
  879. _poolAccessViaMethodOnly = value;
  880. #endif
  881. }
  882. void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv);
  883. void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv) {
  884. void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (__bridge id)sqlite3_user_data(context);
  885. block(context, argc, argv);
  886. }
  887. - (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(sqlite3_context *context, int argc, sqlite3_value **argv))block {
  888. if (!_openFunctions) {
  889. _openFunctions = [NSMutableSet new];
  890. }
  891. id b = FMDBReturnAutoreleased([block copy]);
  892. [_openFunctions addObject:b];
  893. /* I tried adding custom functions to release the block when the connection is destroyed- but they seemed to never be called, so we use _openFunctions to store the values instead. */
  894. sqlite3_create_function([self sqliteHandle], [name UTF8String], count, SQLITE_UTF8, (__bridge void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00);
  895. }
  896. @end
  897. @implementation FMStatement
  898. @synthesize statement=_statement;
  899. @synthesize query=_query;
  900. @synthesize useCount=_useCount;
  901. - (void)finalize {
  902. [self close];
  903. [super finalize];
  904. }
  905. - (void)dealloc {
  906. [self close];
  907. FMDBRelease(_query);
  908. #if ! __has_feature(objc_arc)
  909. [super dealloc];
  910. #endif
  911. }
  912. - (void)close {
  913. if (_statement) {
  914. sqlite3_finalize(_statement);
  915. _statement = 0x00;
  916. }
  917. }
  918. - (void)reset {
  919. if (_statement) {
  920. sqlite3_reset(_statement);
  921. }
  922. }
  923. - (NSString*)description {
  924. return [NSString stringWithFormat:@"%@ %d hit(s) for query %@", [super description], _useCount, _query];
  925. }
  926. @end