FMDatabase.m 36 KB

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