PageRenderTime 28ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/io/FMDB/FMDatabase.m

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