PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/TeamTalk/Pods/FMDB/src/fmdb/FMDatabase.m

https://gitlab.com/lisit1003/TTiOSClient
Objective C | 1413 lines | 1011 code | 344 blank | 58 comment | 255 complexity | 586bd63e051e2f882fda8f9c9d877567 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 checkedOut=_checkedOut;
  13. @synthesize traceExecution=_traceExecution;
  14. #pragma mark FMDatabase instantiation and deallocation
  15. + (instancetype)databaseWithPath:(NSString*)aPath {
  16. return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]);
  17. }
  18. - (instancetype)init {
  19. return [self initWithPath:nil];
  20. }
  21. - (instancetype)initWithPath:(NSString*)aPath {
  22. assert(sqlite3_threadsafe()); // whoa there big boy- gotta make sure sqlite it happy with what we're going to do.
  23. self = [super init];
  24. if (self) {
  25. _databasePath = [aPath copy];
  26. _openResultSets = [[NSMutableSet alloc] init];
  27. _db = nil;
  28. _logsErrors = YES;
  29. _crashOnErrors = NO;
  30. _maxBusyRetryTimeInterval = 2;
  31. }
  32. return self;
  33. }
  34. - (void)finalize {
  35. [self close];
  36. [super finalize];
  37. }
  38. - (void)dealloc {
  39. [self close];
  40. FMDBRelease(_openResultSets);
  41. FMDBRelease(_cachedStatements);
  42. FMDBRelease(_dateFormat);
  43. FMDBRelease(_databasePath);
  44. FMDBRelease(_openFunctions);
  45. #if ! __has_feature(objc_arc)
  46. [super dealloc];
  47. #endif
  48. }
  49. - (NSString *)databasePath {
  50. return _databasePath;
  51. }
  52. + (NSString*)FMDBUserVersion {
  53. return @"2.3";
  54. }
  55. // returns 0x0230 for version 2.3. This makes it super easy to do things like:
  56. // /* need to make sure to do X with FMDB version 2.3 or later */
  57. // if ([FMDatabase FMDBVersion] >= 0x0230) { … }
  58. + (SInt32)FMDBVersion {
  59. // we go through these hoops so that we only have to change the version number in a single spot.
  60. static dispatch_once_t once;
  61. static SInt32 FMDBVersionVal = 0;
  62. dispatch_once(&once, ^{
  63. NSString *prodVersion = [self FMDBUserVersion];
  64. if ([[prodVersion componentsSeparatedByString:@"."] count] < 3) {
  65. prodVersion = [prodVersion stringByAppendingString:@".0"];
  66. }
  67. NSString *junk = [prodVersion stringByReplacingOccurrencesOfString:@"." withString:@""];
  68. char *e = nil;
  69. FMDBVersionVal = (int) strtoul([junk UTF8String], &e, 16);
  70. });
  71. return FMDBVersionVal;
  72. }
  73. #pragma mark SQLite information
  74. + (NSString*)sqliteLibVersion {
  75. return [NSString stringWithFormat:@"%s", sqlite3_libversion()];
  76. }
  77. + (BOOL)isSQLiteThreadSafe {
  78. // make sure to read the sqlite headers on this guy!
  79. return sqlite3_threadsafe() != 0;
  80. }
  81. - (sqlite3*)sqliteHandle {
  82. return _db;
  83. }
  84. - (const char*)sqlitePath {
  85. if (!_databasePath) {
  86. return ":memory:";
  87. }
  88. if ([_databasePath length] == 0) {
  89. return ""; // this creates a temporary database (it's an sqlite thing).
  90. }
  91. return [_databasePath fileSystemRepresentation];
  92. }
  93. #pragma mark Open and close database
  94. - (BOOL)open {
  95. if (_db) {
  96. return YES;
  97. }
  98. int err = sqlite3_open([self sqlitePath], &_db );
  99. if(err != SQLITE_OK) {
  100. NSLog(@"error opening!: %d", err);
  101. return NO;
  102. }
  103. if (_maxBusyRetryTimeInterval > 0.0) {
  104. // set the handler
  105. [self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval];
  106. }
  107. return YES;
  108. }
  109. #if SQLITE_VERSION_NUMBER >= 3005000
  110. - (BOOL)openWithFlags:(int)flags {
  111. if (_db) {
  112. return YES;
  113. }
  114. int err = sqlite3_open_v2([self sqlitePath], &_db, flags, NULL /* Name of VFS module to use */);
  115. if(err != SQLITE_OK) {
  116. NSLog(@"error opening!: %d", err);
  117. return NO;
  118. }
  119. if (_maxBusyRetryTimeInterval > 0.0) {
  120. // set the handler
  121. [self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval];
  122. }
  123. return YES;
  124. }
  125. #endif
  126. - (BOOL)close {
  127. [self clearCachedStatements];
  128. [self closeOpenResultSets];
  129. if (!_db) {
  130. return YES;
  131. }
  132. int rc;
  133. BOOL retry;
  134. BOOL triedFinalizingOpenStatements = NO;
  135. do {
  136. retry = NO;
  137. rc = sqlite3_close(_db);
  138. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  139. if (!triedFinalizingOpenStatements) {
  140. triedFinalizingOpenStatements = YES;
  141. sqlite3_stmt *pStmt;
  142. while ((pStmt = sqlite3_next_stmt(_db, nil)) !=0) {
  143. NSLog(@"Closing leaked statement");
  144. sqlite3_finalize(pStmt);
  145. retry = YES;
  146. }
  147. }
  148. }
  149. else if (SQLITE_OK != rc) {
  150. NSLog(@"error closing!: %d", rc);
  151. }
  152. }
  153. while (retry);
  154. _db = nil;
  155. return YES;
  156. }
  157. #pragma mark Busy handler routines
  158. // NOTE: appledoc seems to choke on this function for some reason;
  159. // so when generating documentation, you might want to ignore the
  160. // .m files so that it only documents the public interfaces outlined
  161. // in the .h files.
  162. //
  163. // This is a known appledoc bug that it has problems with C functions
  164. // within a class implementation, but for some reason, only this
  165. // C function causes problems; the rest don't. Anyway, ignoring the .m
  166. // files with appledoc will prevent this problem from occurring.
  167. static int FMDBDatabaseBusyHandler(void *f, int count) {
  168. FMDatabase *self = (__bridge FMDatabase*)f;
  169. if (count == 0) {
  170. self->_startBusyRetryTime = [NSDate timeIntervalSinceReferenceDate];
  171. return 1;
  172. }
  173. NSTimeInterval delta = [NSDate timeIntervalSinceReferenceDate] - (self->_startBusyRetryTime);
  174. if (delta < [self maxBusyRetryTimeInterval]) {
  175. sqlite3_sleep(50); // milliseconds
  176. return 1;
  177. }
  178. return 0;
  179. }
  180. - (void)setMaxBusyRetryTimeInterval:(NSTimeInterval)timeout {
  181. _maxBusyRetryTimeInterval = timeout;
  182. if (!_db) {
  183. return;
  184. }
  185. if (timeout > 0) {
  186. sqlite3_busy_handler(_db, &FMDBDatabaseBusyHandler, (__bridge void *)(self));
  187. }
  188. else {
  189. // turn it off otherwise
  190. sqlite3_busy_handler(_db, nil, nil);
  191. }
  192. }
  193. - (NSTimeInterval)maxBusyRetryTimeInterval {
  194. return _maxBusyRetryTimeInterval;
  195. }
  196. // we no longer make busyRetryTimeout public
  197. // but for folks who don't bother noticing that the interface to FMDatabase changed,
  198. // we'll still implement the method so they don't get suprise crashes
  199. - (int)busyRetryTimeout {
  200. NSLog(@"%s:%d", __FUNCTION__, __LINE__);
  201. NSLog(@"FMDB: busyRetryTimeout no longer works, please use maxBusyRetryTimeInterval");
  202. return -1;
  203. }
  204. - (void)setBusyRetryTimeout:(int)i {
  205. NSLog(@"%s:%d", __FUNCTION__, __LINE__);
  206. NSLog(@"FMDB: setBusyRetryTimeout does nothing, please use setMaxBusyRetryTimeInterval:");
  207. }
  208. #pragma mark Result set functions
  209. - (BOOL)hasOpenResultSets {
  210. return [_openResultSets count] > 0;
  211. }
  212. - (void)closeOpenResultSets {
  213. //Copy the set so we don't get mutation errors
  214. NSSet *openSetCopy = FMDBReturnAutoreleased([_openResultSets copy]);
  215. for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) {
  216. FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue];
  217. [rs setParentDB:nil];
  218. [rs close];
  219. [_openResultSets removeObject:rsInWrappedInATastyValueMeal];
  220. }
  221. }
  222. - (void)resultSetDidClose:(FMResultSet *)resultSet {
  223. NSValue *setValue = [NSValue valueWithNonretainedObject:resultSet];
  224. [_openResultSets removeObject:setValue];
  225. }
  226. #pragma mark Cached statements
  227. - (void)clearCachedStatements {
  228. for (NSMutableSet *statements in [_cachedStatements objectEnumerator]) {
  229. [statements makeObjectsPerformSelector:@selector(close)];
  230. }
  231. [_cachedStatements removeAllObjects];
  232. }
  233. - (FMStatement*)cachedStatementForQuery:(NSString*)query {
  234. NSMutableSet* statements = [_cachedStatements objectForKey:query];
  235. return [[statements objectsPassingTest:^BOOL(FMStatement* statement, BOOL *stop) {
  236. *stop = ![statement inUse];
  237. return *stop;
  238. }] anyObject];
  239. }
  240. - (void)setCachedStatement:(FMStatement*)statement forQuery:(NSString*)query {
  241. query = [query copy]; // in case we got handed in a mutable string...
  242. [statement setQuery:query];
  243. NSMutableSet* statements = [_cachedStatements objectForKey:query];
  244. if (!statements) {
  245. statements = [NSMutableSet set];
  246. }
  247. [statements addObject:statement];
  248. [_cachedStatements setObject:statements forKey:query];
  249. FMDBRelease(query);
  250. }
  251. #pragma mark Key routines
  252. - (BOOL)rekey:(NSString*)key {
  253. NSData *keyData = [NSData dataWithBytes:(void *)[key UTF8String] length:(NSUInteger)strlen([key UTF8String])];
  254. return [self rekeyWithData:keyData];
  255. }
  256. - (BOOL)rekeyWithData:(NSData *)keyData {
  257. #ifdef SQLITE_HAS_CODEC
  258. if (!keyData) {
  259. return NO;
  260. }
  261. int rc = sqlite3_rekey(_db, [keyData bytes], (int)[keyData length]);
  262. if (rc != SQLITE_OK) {
  263. NSLog(@"error on rekey: %d", rc);
  264. NSLog(@"%@", [self lastErrorMessage]);
  265. }
  266. return (rc == SQLITE_OK);
  267. #else
  268. return NO;
  269. #endif
  270. }
  271. - (BOOL)setKey:(NSString*)key {
  272. NSData *keyData = [NSData dataWithBytes:[key UTF8String] length:(NSUInteger)strlen([key UTF8String])];
  273. return [self setKeyWithData:keyData];
  274. }
  275. - (BOOL)setKeyWithData:(NSData *)keyData {
  276. #ifdef SQLITE_HAS_CODEC
  277. if (!keyData) {
  278. return NO;
  279. }
  280. int rc = sqlite3_key(_db, [keyData bytes], (int)[keyData length]);
  281. return (rc == SQLITE_OK);
  282. #else
  283. return NO;
  284. #endif
  285. }
  286. #pragma mark Date routines
  287. + (NSDateFormatter *)storeableDateFormat:(NSString *)format {
  288. NSDateFormatter *result = FMDBReturnAutoreleased([[NSDateFormatter alloc] init]);
  289. result.dateFormat = format;
  290. result.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
  291. result.locale = FMDBReturnAutoreleased([[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]);
  292. return result;
  293. }
  294. - (BOOL)hasDateFormatter {
  295. return _dateFormat != nil;
  296. }
  297. - (void)setDateFormat:(NSDateFormatter *)format {
  298. FMDBAutorelease(_dateFormat);
  299. _dateFormat = FMDBReturnRetained(format);
  300. }
  301. - (NSDate *)dateFromString:(NSString *)s {
  302. return [_dateFormat dateFromString:s];
  303. }
  304. - (NSString *)stringFromDate:(NSDate *)date {
  305. return [_dateFormat stringFromDate:date];
  306. }
  307. #pragma mark State of database
  308. - (BOOL)goodConnection {
  309. if (!_db) {
  310. return NO;
  311. }
  312. FMResultSet *rs = [self executeQuery:@"select name from sqlite_master where type='table'"];
  313. if (rs) {
  314. [rs close];
  315. return YES;
  316. }
  317. return NO;
  318. }
  319. - (void)warnInUse {
  320. NSLog(@"The FMDatabase %@ is currently in use.", self);
  321. #ifndef NS_BLOCK_ASSERTIONS
  322. if (_crashOnErrors) {
  323. NSAssert(false, @"The FMDatabase %@ is currently in use.", self);
  324. abort();
  325. }
  326. #endif
  327. }
  328. - (BOOL)databaseExists {
  329. if (!_db) {
  330. NSLog(@"The FMDatabase %@ is not open.", self);
  331. #ifndef NS_BLOCK_ASSERTIONS
  332. if (_crashOnErrors) {
  333. NSAssert(false, @"The FMDatabase %@ is not open.", self);
  334. abort();
  335. }
  336. #endif
  337. return NO;
  338. }
  339. return YES;
  340. }
  341. #pragma mark Error routines
  342. - (NSString*)lastErrorMessage {
  343. return [NSString stringWithUTF8String:sqlite3_errmsg(_db)];
  344. }
  345. - (BOOL)hadError {
  346. int lastErrCode = [self lastErrorCode];
  347. return (lastErrCode > SQLITE_OK && lastErrCode < SQLITE_ROW);
  348. }
  349. - (int)lastErrorCode {
  350. return sqlite3_errcode(_db);
  351. }
  352. - (NSError*)errorWithMessage:(NSString*)message {
  353. NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey];
  354. return [NSError errorWithDomain:@"FMDatabase" code:sqlite3_errcode(_db) userInfo:errorMessage];
  355. }
  356. - (NSError*)lastError {
  357. return [self errorWithMessage:[self lastErrorMessage]];
  358. }
  359. #pragma mark Update information routines
  360. - (sqlite_int64)lastInsertRowId {
  361. if (_isExecutingStatement) {
  362. [self warnInUse];
  363. return NO;
  364. }
  365. _isExecutingStatement = YES;
  366. sqlite_int64 ret = sqlite3_last_insert_rowid(_db);
  367. _isExecutingStatement = NO;
  368. return ret;
  369. }
  370. - (int)changes {
  371. if (_isExecutingStatement) {
  372. [self warnInUse];
  373. return 0;
  374. }
  375. _isExecutingStatement = YES;
  376. int ret = sqlite3_changes(_db);
  377. _isExecutingStatement = NO;
  378. return ret;
  379. }
  380. #pragma mark SQL manipulation
  381. - (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt {
  382. if ((!obj) || ((NSNull *)obj == [NSNull null])) {
  383. sqlite3_bind_null(pStmt, idx);
  384. }
  385. // FIXME - someday check the return codes on these binds.
  386. else if ([obj isKindOfClass:[NSData class]]) {
  387. const void *bytes = [obj bytes];
  388. if (!bytes) {
  389. // it's an empty NSData object, aka [NSData data].
  390. // Don't pass a NULL pointer, or sqlite will bind a SQL null instead of a blob.
  391. bytes = "";
  392. }
  393. sqlite3_bind_blob(pStmt, idx, bytes, (int)[obj length], SQLITE_STATIC);
  394. }
  395. else if ([obj isKindOfClass:[NSDate class]]) {
  396. if (self.hasDateFormatter)
  397. sqlite3_bind_text(pStmt, idx, [[self stringFromDate:obj] UTF8String], -1, SQLITE_STATIC);
  398. else
  399. sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]);
  400. }
  401. else if ([obj isKindOfClass:[NSNumber class]]) {
  402. if (strcmp([obj objCType], @encode(BOOL)) == 0) {
  403. sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0));
  404. }
  405. else if (strcmp([obj objCType], @encode(char)) == 0) {
  406. sqlite3_bind_int(pStmt, idx, [obj charValue]);
  407. }
  408. else if (strcmp([obj objCType], @encode(unsigned char)) == 0) {
  409. sqlite3_bind_int(pStmt, idx, [obj unsignedCharValue]);
  410. }
  411. else if (strcmp([obj objCType], @encode(short)) == 0) {
  412. sqlite3_bind_int(pStmt, idx, [obj shortValue]);
  413. }
  414. else if (strcmp([obj objCType], @encode(unsigned short)) == 0) {
  415. sqlite3_bind_int(pStmt, idx, [obj unsignedShortValue]);
  416. }
  417. else if (strcmp([obj objCType], @encode(int)) == 0) {
  418. sqlite3_bind_int(pStmt, idx, [obj intValue]);
  419. }
  420. else if (strcmp([obj objCType], @encode(unsigned int)) == 0) {
  421. sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedIntValue]);
  422. }
  423. else if (strcmp([obj objCType], @encode(long)) == 0) {
  424. sqlite3_bind_int64(pStmt, idx, [obj longValue]);
  425. }
  426. else if (strcmp([obj objCType], @encode(unsigned long)) == 0) {
  427. sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedLongValue]);
  428. }
  429. else if (strcmp([obj objCType], @encode(long long)) == 0) {
  430. sqlite3_bind_int64(pStmt, idx, [obj longLongValue]);
  431. }
  432. else if (strcmp([obj objCType], @encode(unsigned long long)) == 0) {
  433. sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedLongLongValue]);
  434. }
  435. else if (strcmp([obj objCType], @encode(float)) == 0) {
  436. sqlite3_bind_double(pStmt, idx, [obj floatValue]);
  437. }
  438. else if (strcmp([obj objCType], @encode(double)) == 0) {
  439. sqlite3_bind_double(pStmt, idx, [obj doubleValue]);
  440. }
  441. else {
  442. sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
  443. }
  444. }
  445. else {
  446. sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
  447. }
  448. }
  449. - (void)extractSQL:(NSString *)sql argumentsList:(va_list)args intoString:(NSMutableString *)cleanedSQL arguments:(NSMutableArray *)arguments {
  450. NSUInteger length = [sql length];
  451. unichar last = '\0';
  452. for (NSUInteger i = 0; i < length; ++i) {
  453. id arg = nil;
  454. unichar current = [sql characterAtIndex:i];
  455. unichar add = current;
  456. if (last == '%') {
  457. switch (current) {
  458. case '@':
  459. arg = va_arg(args, id);
  460. break;
  461. case 'c':
  462. // warning: second argument to 'va_arg' is of promotable type 'char'; this va_arg has undefined behavior because arguments will be promoted to 'int'
  463. arg = [NSString stringWithFormat:@"%c", va_arg(args, int)];
  464. break;
  465. case 's':
  466. arg = [NSString stringWithUTF8String:va_arg(args, char*)];
  467. break;
  468. case 'd':
  469. case 'D':
  470. case 'i':
  471. arg = [NSNumber numberWithInt:va_arg(args, int)];
  472. break;
  473. case 'u':
  474. case 'U':
  475. arg = [NSNumber numberWithUnsignedInt:va_arg(args, unsigned int)];
  476. break;
  477. case 'h':
  478. i++;
  479. if (i < length && [sql characterAtIndex:i] == 'i') {
  480. // warning: second argument to 'va_arg' is of promotable type 'short'; this va_arg has undefined behavior because arguments will be promoted to 'int'
  481. arg = [NSNumber numberWithShort:(short)(va_arg(args, int))];
  482. }
  483. else if (i < length && [sql characterAtIndex:i] == 'u') {
  484. // 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'
  485. arg = [NSNumber numberWithUnsignedShort:(unsigned short)(va_arg(args, uint))];
  486. }
  487. else {
  488. i--;
  489. }
  490. break;
  491. case 'q':
  492. i++;
  493. if (i < length && [sql characterAtIndex:i] == 'i') {
  494. arg = [NSNumber numberWithLongLong:va_arg(args, long long)];
  495. }
  496. else if (i < length && [sql characterAtIndex:i] == 'u') {
  497. arg = [NSNumber numberWithUnsignedLongLong:va_arg(args, unsigned long long)];
  498. }
  499. else {
  500. i--;
  501. }
  502. break;
  503. case 'f':
  504. arg = [NSNumber numberWithDouble:va_arg(args, double)];
  505. break;
  506. case 'g':
  507. // warning: second argument to 'va_arg' is of promotable type 'float'; this va_arg has undefined behavior because arguments will be promoted to 'double'
  508. arg = [NSNumber numberWithFloat:(float)(va_arg(args, double))];
  509. break;
  510. case 'l':
  511. i++;
  512. if (i < length) {
  513. unichar next = [sql characterAtIndex:i];
  514. if (next == 'l') {
  515. i++;
  516. if (i < length && [sql characterAtIndex:i] == 'd') {
  517. //%lld
  518. arg = [NSNumber numberWithLongLong:va_arg(args, long long)];
  519. }
  520. else if (i < length && [sql characterAtIndex:i] == 'u') {
  521. //%llu
  522. arg = [NSNumber numberWithUnsignedLongLong:va_arg(args, unsigned long long)];
  523. }
  524. else {
  525. i--;
  526. }
  527. }
  528. else if (next == 'd') {
  529. //%ld
  530. arg = [NSNumber numberWithLong:va_arg(args, long)];
  531. }
  532. else if (next == 'u') {
  533. //%lu
  534. arg = [NSNumber numberWithUnsignedLong:va_arg(args, unsigned long)];
  535. }
  536. else {
  537. i--;
  538. }
  539. }
  540. else {
  541. i--;
  542. }
  543. break;
  544. default:
  545. // something else that we can't interpret. just pass it on through like normal
  546. break;
  547. }
  548. }
  549. else if (current == '%') {
  550. // percent sign; skip this character
  551. add = '\0';
  552. }
  553. if (arg != nil) {
  554. [cleanedSQL appendString:@"?"];
  555. [arguments addObject:arg];
  556. }
  557. else if (add == (unichar)'@' && last == (unichar) '%') {
  558. [cleanedSQL appendFormat:@"NULL"];
  559. }
  560. else if (add != '\0') {
  561. [cleanedSQL appendFormat:@"%C", add];
  562. }
  563. last = current;
  564. }
  565. }
  566. #pragma mark Execute queries
  567. - (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments {
  568. return [self executeQuery:sql withArgumentsInArray:nil orDictionary:arguments orVAList:nil];
  569. }
  570. - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
  571. if (![self databaseExists]) {
  572. return 0x00;
  573. }
  574. if (_isExecutingStatement) {
  575. [self warnInUse];
  576. return 0x00;
  577. }
  578. _isExecutingStatement = YES;
  579. int rc = 0x00;
  580. sqlite3_stmt *pStmt = 0x00;
  581. FMStatement *statement = 0x00;
  582. FMResultSet *rs = 0x00;
  583. if (_traceExecution && sql) {
  584. NSLog(@"%@ executeQuery: %@", self, sql);
  585. }
  586. if (_shouldCacheStatements) {
  587. statement = [self cachedStatementForQuery:sql];
  588. pStmt = statement ? [statement statement] : 0x00;
  589. [statement reset];
  590. }
  591. if (!pStmt) {
  592. rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
  593. if (SQLITE_OK != rc) {
  594. if (_logsErrors) {
  595. NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  596. NSLog(@"DB Query: %@", sql);
  597. NSLog(@"DB Path: %@", _databasePath);
  598. }
  599. if (_crashOnErrors) {
  600. NSAssert(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  601. abort();
  602. }
  603. sqlite3_finalize(pStmt);
  604. _isExecutingStatement = NO;
  605. return nil;
  606. }
  607. }
  608. id obj;
  609. int idx = 0;
  610. int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!)
  611. // If dictionaryArgs is passed in, that means we are using sqlite's named parameter support
  612. if (dictionaryArgs) {
  613. for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
  614. // Prefix the key with a colon.
  615. NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
  616. // Get the index for the parameter name.
  617. int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
  618. FMDBRelease(parameterName);
  619. if (namedIdx > 0) {
  620. // Standard binding from here.
  621. [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
  622. // increment the binding count, so our check below works out
  623. idx++;
  624. }
  625. else {
  626. NSLog(@"Could not find index for %@", dictionaryKey);
  627. }
  628. }
  629. }
  630. else {
  631. while (idx < queryCount) {
  632. if (arrayArgs && idx < (int)[arrayArgs count]) {
  633. obj = [arrayArgs objectAtIndex:(NSUInteger)idx];
  634. }
  635. else if (args) {
  636. obj = va_arg(args, id);
  637. }
  638. else {
  639. //We ran out of arguments
  640. break;
  641. }
  642. if (_traceExecution) {
  643. if ([obj isKindOfClass:[NSData class]]) {
  644. NSLog(@"data: %ld bytes", (unsigned long)[(NSData*)obj length]);
  645. }
  646. else {
  647. NSLog(@"obj: %@", obj);
  648. }
  649. }
  650. idx++;
  651. [self bindObject:obj toColumn:idx inStatement:pStmt];
  652. }
  653. }
  654. if (idx != queryCount) {
  655. NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)");
  656. sqlite3_finalize(pStmt);
  657. _isExecutingStatement = NO;
  658. return nil;
  659. }
  660. FMDBRetain(statement); // to balance the release below
  661. if (!statement) {
  662. statement = [[FMStatement alloc] init];
  663. [statement setStatement:pStmt];
  664. if (_shouldCacheStatements && sql) {
  665. [self setCachedStatement:statement forQuery:sql];
  666. }
  667. }
  668. // the statement gets closed in rs's dealloc or [rs close];
  669. rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self];
  670. [rs setQuery:sql];
  671. NSValue *openResultSet = [NSValue valueWithNonretainedObject:rs];
  672. [_openResultSets addObject:openResultSet];
  673. [statement setUseCount:[statement useCount] + 1];
  674. FMDBRelease(statement);
  675. _isExecutingStatement = NO;
  676. return rs;
  677. }
  678. - (FMResultSet *)executeQuery:(NSString*)sql, ... {
  679. va_list args;
  680. va_start(args, sql);
  681. id result = [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args];
  682. va_end(args);
  683. return result;
  684. }
  685. - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... {
  686. va_list args;
  687. va_start(args, format);
  688. NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]];
  689. NSMutableArray *arguments = [NSMutableArray array];
  690. [self extractSQL:format argumentsList:args intoString:sql arguments:arguments];
  691. va_end(args);
  692. return [self executeQuery:sql withArgumentsInArray:arguments];
  693. }
  694. - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments {
  695. return [self executeQuery:sql withArgumentsInArray:arguments orDictionary:nil orVAList:nil];
  696. }
  697. - (FMResultSet *)executeQuery:(NSString*)sql withVAList:(va_list)args {
  698. return [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args];
  699. }
  700. #pragma mark Execute updates
  701. - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
  702. if (![self databaseExists]) {
  703. return NO;
  704. }
  705. if (_isExecutingStatement) {
  706. [self warnInUse];
  707. return NO;
  708. }
  709. _isExecutingStatement = YES;
  710. int rc = 0x00;
  711. sqlite3_stmt *pStmt = 0x00;
  712. FMStatement *cachedStmt = 0x00;
  713. if (_traceExecution && sql) {
  714. NSLog(@"%@ executeUpdate: %@", self, sql);
  715. }
  716. if (_shouldCacheStatements) {
  717. cachedStmt = [self cachedStatementForQuery:sql];
  718. pStmt = cachedStmt ? [cachedStmt statement] : 0x00;
  719. [cachedStmt reset];
  720. }
  721. if (!pStmt) {
  722. rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
  723. if (SQLITE_OK != rc) {
  724. if (_logsErrors) {
  725. NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  726. NSLog(@"DB Query: %@", sql);
  727. NSLog(@"DB Path: %@", _databasePath);
  728. }
  729. if (_crashOnErrors) {
  730. NSAssert(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  731. abort();
  732. }
  733. sqlite3_finalize(pStmt);
  734. if (outErr) {
  735. *outErr = [self errorWithMessage:[NSString stringWithUTF8String:sqlite3_errmsg(_db)]];
  736. }
  737. _isExecutingStatement = NO;
  738. return NO;
  739. }
  740. }
  741. id obj;
  742. int idx = 0;
  743. int queryCount = sqlite3_bind_parameter_count(pStmt);
  744. // If dictionaryArgs is passed in, that means we are using sqlite's named parameter support
  745. if (dictionaryArgs) {
  746. for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
  747. // Prefix the key with a colon.
  748. NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
  749. // Get the index for the parameter name.
  750. int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
  751. FMDBRelease(parameterName);
  752. if (namedIdx > 0) {
  753. // Standard binding from here.
  754. [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
  755. // increment the binding count, so our check below works out
  756. idx++;
  757. }
  758. else {
  759. NSLog(@"Could not find index for %@", dictionaryKey);
  760. }
  761. }
  762. }
  763. else {
  764. while (idx < queryCount) {
  765. if (arrayArgs && idx < (int)[arrayArgs count]) {
  766. obj = [arrayArgs objectAtIndex:(NSUInteger)idx];
  767. }
  768. else if (args) {
  769. obj = va_arg(args, id);
  770. }
  771. else {
  772. //We ran out of arguments
  773. break;
  774. }
  775. if (_traceExecution) {
  776. if ([obj isKindOfClass:[NSData class]]) {
  777. NSLog(@"data: %ld bytes", (unsigned long)[(NSData*)obj length]);
  778. }
  779. else {
  780. NSLog(@"obj: %@", obj);
  781. }
  782. }
  783. idx++;
  784. [self bindObject:obj toColumn:idx inStatement:pStmt];
  785. }
  786. }
  787. if (idx != queryCount) {
  788. NSLog(@"Error: the bind count (%d) is not correct for the # of variables in the query (%d) (%@) (executeUpdate)", idx, queryCount, sql);
  789. sqlite3_finalize(pStmt);
  790. _isExecutingStatement = NO;
  791. return NO;
  792. }
  793. /* Call sqlite3_step() to run the virtual machine. Since the SQL being
  794. ** executed is not a SELECT statement, we assume no data will be returned.
  795. */
  796. rc = sqlite3_step(pStmt);
  797. if (SQLITE_DONE == rc) {
  798. // all is well, let's return.
  799. }
  800. else if (SQLITE_ERROR == rc) {
  801. if (_logsErrors) {
  802. NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db));
  803. NSLog(@"DB Query: %@", sql);
  804. }
  805. }
  806. else if (SQLITE_MISUSE == rc) {
  807. // uh oh.
  808. if (_logsErrors) {
  809. NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db));
  810. NSLog(@"DB Query: %@", sql);
  811. }
  812. }
  813. else {
  814. // wtf?
  815. if (_logsErrors) {
  816. NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db));
  817. NSLog(@"DB Query: %@", sql);
  818. }
  819. }
  820. if (rc == SQLITE_ROW) {
  821. NSAssert(NO, @"A executeUpdate is being called with a query string '%@'", sql);
  822. }
  823. if (_shouldCacheStatements && !cachedStmt) {
  824. cachedStmt = [[FMStatement alloc] init];
  825. [cachedStmt setStatement:pStmt];
  826. [self setCachedStatement:cachedStmt forQuery:sql];
  827. FMDBRelease(cachedStmt);
  828. }
  829. int closeErrorCode;
  830. if (cachedStmt) {
  831. [cachedStmt setUseCount:[cachedStmt useCount] + 1];
  832. closeErrorCode = sqlite3_reset(pStmt);
  833. }
  834. else {
  835. /* Finalize the virtual machine. This releases all memory and other
  836. ** resources allocated by the sqlite3_prepare() call above.
  837. */
  838. closeErrorCode = sqlite3_finalize(pStmt);
  839. }
  840. if (closeErrorCode != SQLITE_OK) {
  841. if (_logsErrors) {
  842. NSLog(@"Unknown error finalizing or resetting statement (%d: %s)", closeErrorCode, sqlite3_errmsg(_db));
  843. NSLog(@"DB Query: %@", sql);
  844. }
  845. }
  846. _isExecutingStatement = NO;
  847. return (rc == SQLITE_DONE || rc == SQLITE_OK);
  848. }
  849. - (BOOL)executeUpdate:(NSString*)sql, ... {
  850. va_list args;
  851. va_start(args, sql);
  852. BOOL result = [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:nil orVAList:args];
  853. va_end(args);
  854. return result;
  855. }
  856. - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments {
  857. return [self executeUpdate:sql error:nil withArgumentsInArray:arguments orDictionary:nil orVAList:nil];
  858. }
  859. - (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments {
  860. return [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:arguments orVAList:nil];
  861. }
  862. - (BOOL)executeUpdate:(NSString*)sql withVAList:(va_list)args {
  863. return [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:nil orVAList:args];
  864. }
  865. - (BOOL)executeUpdateWithFormat:(NSString*)format, ... {
  866. va_list args;
  867. va_start(args, format);
  868. NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]];
  869. NSMutableArray *arguments = [NSMutableArray array];
  870. [self extractSQL:format argumentsList:args intoString:sql arguments:arguments];
  871. va_end(args);
  872. return [self executeUpdate:sql withArgumentsInArray:arguments];
  873. }
  874. int FMDBExecuteBulkSQLCallback(void *theBlockAsVoid, int columns, char **values, char **names); // shhh clang.
  875. int FMDBExecuteBulkSQLCallback(void *theBlockAsVoid, int columns, char **values, char **names) {
  876. if (!theBlockAsVoid) {
  877. return SQLITE_OK;
  878. }
  879. int (^execCallbackBlock)(NSDictionary *resultsDictionary) = (__bridge int (^)(NSDictionary *__strong))(theBlockAsVoid);
  880. NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:(NSUInteger)columns];
  881. for (NSInteger i = 0; i < columns; i++) {
  882. NSString *key = [NSString stringWithUTF8String:names[i]];
  883. id value = values[i] ? [NSString stringWithUTF8String:values[i]] : [NSNull null];
  884. [dictionary setObject:value forKey:key];
  885. }
  886. return execCallbackBlock(dictionary);
  887. }
  888. - (BOOL)executeStatements:(NSString *)sql {
  889. return [self executeStatements:sql withResultBlock:nil];
  890. }
  891. - (BOOL)executeStatements:(NSString *)sql withResultBlock:(FMDBExecuteStatementsCallbackBlock)block {
  892. int rc;
  893. char *errmsg = nil;
  894. rc = sqlite3_exec([self sqliteHandle], [sql UTF8String], block ? FMDBExecuteBulkSQLCallback : nil, (__bridge void *)(block), &errmsg);
  895. if (errmsg && [self logsErrors]) {
  896. NSLog(@"Error inserting batch: %s", errmsg);
  897. sqlite3_free(errmsg);
  898. }
  899. return (rc == SQLITE_OK);
  900. }
  901. - (BOOL)executeUpdate:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ... {
  902. va_list args;
  903. va_start(args, outErr);
  904. BOOL result = [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:args];
  905. va_end(args);
  906. return result;
  907. }
  908. #pragma clang diagnostic push
  909. #pragma clang diagnostic ignored "-Wdeprecated-implementations"
  910. - (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ... {
  911. va_list args;
  912. va_start(args, outErr);
  913. BOOL result = [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:args];
  914. va_end(args);
  915. return result;
  916. }
  917. #pragma clang diagnostic pop
  918. #pragma mark Transactions
  919. - (BOOL)rollback {
  920. BOOL b = [self executeUpdate:@"rollback transaction"];
  921. if (b) {
  922. _inTransaction = NO;
  923. }
  924. return b;
  925. }
  926. - (BOOL)commit {
  927. BOOL b = [self executeUpdate:@"commit transaction"];
  928. if (b) {
  929. _inTransaction = NO;
  930. }
  931. return b;
  932. }
  933. - (BOOL)beginDeferredTransaction {
  934. BOOL b = [self executeUpdate:@"begin deferred transaction"];
  935. if (b) {
  936. _inTransaction = YES;
  937. }
  938. return b;
  939. }
  940. - (BOOL)beginTransaction {
  941. BOOL b = [self executeUpdate:@"begin exclusive transaction"];
  942. if (b) {
  943. _inTransaction = YES;
  944. }
  945. return b;
  946. }
  947. - (BOOL)inTransaction {
  948. return _inTransaction;
  949. }
  950. #if SQLITE_VERSION_NUMBER >= 3007000
  951. static NSString *FMDBEscapeSavePointName(NSString *savepointName) {
  952. return [savepointName stringByReplacingOccurrencesOfString:@"'" withString:@"''"];
  953. }
  954. - (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr {
  955. NSParameterAssert(name);
  956. NSString *sql = [NSString stringWithFormat:@"savepoint '%@';", FMDBEscapeSavePointName(name)];
  957. if (![self executeUpdate:sql]) {
  958. if (outErr) {
  959. *outErr = [self lastError];
  960. }
  961. return NO;
  962. }
  963. return YES;
  964. }
  965. - (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr {
  966. NSParameterAssert(name);
  967. NSString *sql = [NSString stringWithFormat:@"release savepoint '%@';", FMDBEscapeSavePointName(name)];
  968. BOOL worked = [self executeUpdate:sql];
  969. if (!worked && outErr) {
  970. *outErr = [self lastError];
  971. }
  972. return worked;
  973. }
  974. - (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr {
  975. NSParameterAssert(name);
  976. NSString *sql = [NSString stringWithFormat:@"rollback transaction to savepoint '%@';", FMDBEscapeSavePointName(name)];
  977. BOOL worked = [self executeUpdate:sql];
  978. if (!worked && outErr) {
  979. *outErr = [self lastError];
  980. }
  981. return worked;
  982. }
  983. - (NSError*)inSavePoint:(void (^)(BOOL *rollback))block {
  984. static unsigned long savePointIdx = 0;
  985. NSString *name = [NSString stringWithFormat:@"dbSavePoint%ld", savePointIdx++];
  986. BOOL shouldRollback = NO;
  987. NSError *err = 0x00;
  988. if (![self startSavePointWithName:name error:&err]) {
  989. return err;
  990. }
  991. block(&shouldRollback);
  992. if (shouldRollback) {
  993. // We need to rollback and release this savepoint to remove it
  994. [self rollbackToSavePointWithName:name error:&err];
  995. }
  996. [self releaseSavePointWithName:name error:&err];
  997. return err;
  998. }
  999. #endif
  1000. #pragma mark Cache statements
  1001. - (BOOL)shouldCacheStatements {
  1002. return _shouldCacheStatements;
  1003. }
  1004. - (void)setShouldCacheStatements:(BOOL)value {
  1005. _shouldCacheStatements = value;
  1006. if (_shouldCacheStatements && !_cachedStatements) {
  1007. [self setCachedStatements:[NSMutableDictionary dictionary]];
  1008. }
  1009. if (!_shouldCacheStatements) {
  1010. [self setCachedStatements:nil];
  1011. }
  1012. }
  1013. #pragma mark Callback function
  1014. void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv); // -Wmissing-prototypes
  1015. void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv) {
  1016. #if ! __has_feature(objc_arc)
  1017. void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (id)sqlite3_user_data(context);
  1018. #else
  1019. void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (__bridge id)sqlite3_user_data(context);
  1020. #endif
  1021. block(context, argc, argv);
  1022. }
  1023. - (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(sqlite3_context *context, int argc, sqlite3_value **argv))block {
  1024. if (!_openFunctions) {
  1025. _openFunctions = [NSMutableSet new];
  1026. }
  1027. id b = FMDBReturnAutoreleased([block copy]);
  1028. [_openFunctions addObject:b];
  1029. /* 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. */
  1030. #if ! __has_feature(objc_arc)
  1031. sqlite3_create_function([self sqliteHandle], [name UTF8String], count, SQLITE_UTF8, (void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00);
  1032. #else
  1033. sqlite3_create_function([self sqliteHandle], [name UTF8String], count, SQLITE_UTF8, (__bridge void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00);
  1034. #endif
  1035. }
  1036. @end
  1037. @implementation FMStatement
  1038. @synthesize statement=_statement;
  1039. @synthesize query=_query;
  1040. @synthesize useCount=_useCount;
  1041. @synthesize inUse=_inUse;
  1042. - (void)finalize {
  1043. [self close];
  1044. [super finalize];
  1045. }
  1046. - (void)dealloc {
  1047. [self close];
  1048. FMDBRelease(_query);
  1049. #if ! __has_feature(objc_arc)
  1050. [super dealloc];
  1051. #endif
  1052. }
  1053. - (void)close {
  1054. if (_statement) {
  1055. sqlite3_finalize(_statement);
  1056. _statement = 0x00;
  1057. }
  1058. _inUse = NO;
  1059. }
  1060. - (void)reset {
  1061. if (_statement) {
  1062. sqlite3_reset(_statement);
  1063. }
  1064. _inUse = NO;
  1065. }
  1066. - (NSString*)description {
  1067. return [NSString stringWithFormat:@"%@ %ld hit(s) for query %@", [super description], _useCount, _query];
  1068. }
  1069. @end