FMDatabase.m 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219
  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) {
  535. obj = [arrayArgs objectAtIndex:(NSUInteger)idx];
  536. }
  537. else {
  538. obj = va_arg(args, id);
  539. }
  540. if (_traceExecution) {
  541. if ([obj isKindOfClass:[NSData class]]) {
  542. NSLog(@"data: %ld bytes", (unsigned long)[(NSData*)obj length]);
  543. }
  544. else {
  545. NSLog(@"obj: %@", obj);
  546. }
  547. }
  548. idx++;
  549. [self bindObject:obj toColumn:idx inStatement:pStmt];
  550. }
  551. }
  552. if (idx != queryCount) {
  553. NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)");
  554. sqlite3_finalize(pStmt);
  555. _isExecutingStatement = NO;
  556. return nil;
  557. }
  558. FMDBRetain(statement); // to balance the release below
  559. if (!statement) {
  560. statement = [[FMStatement alloc] init];
  561. [statement setStatement:pStmt];
  562. if (_shouldCacheStatements) {
  563. [self setCachedStatement:statement forQuery:sql];
  564. }
  565. }
  566. // the statement gets closed in rs's dealloc or [rs close];
  567. rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self];
  568. [rs setQuery:sql];
  569. NSValue *openResultSet = [NSValue valueWithNonretainedObject:rs];
  570. [_openResultSets addObject:openResultSet];
  571. [statement setUseCount:[statement useCount] + 1];
  572. FMDBRelease(statement);
  573. _isExecutingStatement = NO;
  574. return rs;
  575. }
  576. - (FMResultSet *)executeQuery:(NSString*)sql, ... {
  577. va_list args;
  578. va_start(args, sql);
  579. id result = [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args];
  580. va_end(args);
  581. return result;
  582. }
  583. - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... {
  584. va_list args;
  585. va_start(args, format);
  586. NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]];
  587. NSMutableArray *arguments = [NSMutableArray array];
  588. [self extractSQL:format argumentsList:args intoString:sql arguments:arguments];
  589. va_end(args);
  590. return [self executeQuery:sql withArgumentsInArray:arguments];
  591. }
  592. - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments {
  593. return [self executeQuery:sql withArgumentsInArray:arguments orDictionary:nil orVAList:nil];
  594. }
  595. - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
  596. if (![self databaseExists]) {
  597. return NO;
  598. }
  599. if (_isExecutingStatement) {
  600. [self warnInUse];
  601. return NO;
  602. }
  603. _isExecutingStatement = YES;
  604. int rc = 0x00;
  605. sqlite3_stmt *pStmt = 0x00;
  606. FMStatement *cachedStmt = 0x00;
  607. if (_traceExecution && sql) {
  608. NSLog(@"%@ executeUpdate: %@", self, sql);
  609. }
  610. if (_shouldCacheStatements) {
  611. cachedStmt = [self cachedStatementForQuery:sql];
  612. pStmt = cachedStmt ? [cachedStmt statement] : 0x00;
  613. [cachedStmt reset];
  614. }
  615. int numberOfRetries = 0;
  616. BOOL retry = NO;
  617. if (!pStmt) {
  618. do {
  619. retry = NO;
  620. rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
  621. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  622. retry = YES;
  623. usleep(20);
  624. if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) {
  625. NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
  626. NSLog(@"Database busy");
  627. sqlite3_finalize(pStmt);
  628. _isExecutingStatement = NO;
  629. return NO;
  630. }
  631. }
  632. else if (SQLITE_OK != rc) {
  633. if (_logsErrors) {
  634. NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  635. NSLog(@"DB Query: %@", sql);
  636. NSLog(@"DB Path: %@", _databasePath);
  637. #ifndef NS_BLOCK_ASSERTIONS
  638. if (_crashOnErrors) {
  639. abort();
  640. NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
  641. }
  642. #endif
  643. }
  644. sqlite3_finalize(pStmt);
  645. if (outErr) {
  646. *outErr = [self errorWithMessage:[NSString stringWithUTF8String:sqlite3_errmsg(_db)]];
  647. }
  648. _isExecutingStatement = NO;
  649. return NO;
  650. }
  651. }
  652. while (retry);
  653. }
  654. id obj;
  655. int idx = 0;
  656. int queryCount = sqlite3_bind_parameter_count(pStmt);
  657. // If dictionaryArgs is passed in, that means we are using sqlite's named parameter support
  658. if (dictionaryArgs) {
  659. for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
  660. // Prefix the key with a colon.
  661. NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
  662. // Get the index for the parameter name.
  663. int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
  664. FMDBRelease(parameterName);
  665. if (namedIdx > 0) {
  666. // Standard binding from here.
  667. [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
  668. // increment the binding count, so our check below works out
  669. idx++;
  670. }
  671. else {
  672. NSLog(@"Could not find index for %@", dictionaryKey);
  673. }
  674. }
  675. }
  676. else {
  677. while (idx < queryCount) {
  678. if (arrayArgs) {
  679. obj = [arrayArgs objectAtIndex:(NSUInteger)idx];
  680. }
  681. else {
  682. obj = va_arg(args, id);
  683. }
  684. if (_traceExecution) {
  685. if ([obj isKindOfClass:[NSData class]]) {
  686. NSLog(@"data: %ld bytes", (unsigned long)[(NSData*)obj length]);
  687. }
  688. else {
  689. NSLog(@"obj: %@", obj);
  690. }
  691. }
  692. idx++;
  693. [self bindObject:obj toColumn:idx inStatement:pStmt];
  694. }
  695. }
  696. if (idx != queryCount) {
  697. NSLog(@"Error: the bind count (%d) is not correct for the # of variables in the query (%d) (%@) (executeUpdate)", idx, queryCount, sql);
  698. sqlite3_finalize(pStmt);
  699. _isExecutingStatement = NO;
  700. return NO;
  701. }
  702. /* Call sqlite3_step() to run the virtual machine. Since the SQL being
  703. ** executed is not a SELECT statement, we assume no data will be returned.
  704. */
  705. numberOfRetries = 0;
  706. do {
  707. rc = sqlite3_step(pStmt);
  708. retry = NO;
  709. if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
  710. // this will happen if the db is locked, like if we are doing an update or insert.
  711. // in that case, retry the step... and maybe wait just 10 milliseconds.
  712. retry = YES;
  713. if (SQLITE_LOCKED == rc) {
  714. rc = sqlite3_reset(pStmt);
  715. if (rc != SQLITE_LOCKED) {
  716. NSLog(@"Unexpected result from sqlite3_reset (%d) eu", rc);
  717. }
  718. }
  719. usleep(20);
  720. if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) {
  721. NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
  722. NSLog(@"Database busy");
  723. retry = NO;
  724. }
  725. }
  726. else if (SQLITE_DONE == rc) {
  727. // all is well, let's return.
  728. }
  729. else if (SQLITE_ERROR == rc) {
  730. NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db));
  731. NSLog(@"DB Query: %@", sql);
  732. }
  733. else if (SQLITE_MISUSE == rc) {
  734. // uh oh.
  735. NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db));
  736. NSLog(@"DB Query: %@", sql);
  737. }
  738. else {
  739. // wtf?
  740. NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db));
  741. NSLog(@"DB Query: %@", sql);
  742. }
  743. } while (retry);
  744. if (rc == SQLITE_ROW) {
  745. NSAssert1(NO, @"A executeUpdate is being called with a query string '%@'", sql);
  746. }
  747. if (_shouldCacheStatements && !cachedStmt) {
  748. cachedStmt = [[FMStatement alloc] init];
  749. [cachedStmt setStatement:pStmt];
  750. [self setCachedStatement:cachedStmt forQuery:sql];
  751. FMDBRelease(cachedStmt);
  752. }
  753. int closeErrorCode;
  754. if (cachedStmt) {
  755. [cachedStmt setUseCount:[cachedStmt useCount] + 1];
  756. closeErrorCode = sqlite3_reset(pStmt);
  757. }
  758. else {
  759. /* Finalize the virtual machine. This releases all memory and other
  760. ** resources allocated by the sqlite3_prepare() call above.
  761. */
  762. closeErrorCode = sqlite3_finalize(pStmt);
  763. }
  764. if (closeErrorCode != SQLITE_OK) {
  765. NSLog(@"Unknown error finalizing or resetting statement (%d: %s)", closeErrorCode, sqlite3_errmsg(_db));
  766. NSLog(@"DB Query: %@", sql);
  767. }
  768. _isExecutingStatement = NO;
  769. return (rc == SQLITE_DONE || rc == SQLITE_OK);
  770. }
  771. - (BOOL)executeUpdate:(NSString*)sql, ... {
  772. va_list args;
  773. va_start(args, sql);
  774. BOOL result = [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:nil orVAList:args];
  775. va_end(args);
  776. return result;
  777. }
  778. - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments {
  779. return [self executeUpdate:sql error:nil withArgumentsInArray:arguments orDictionary:nil orVAList:nil];
  780. }
  781. - (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments {
  782. return [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:arguments orVAList:nil];
  783. }
  784. - (BOOL)executeUpdateWithFormat:(NSString*)format, ... {
  785. va_list args;
  786. va_start(args, format);
  787. NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]];
  788. NSMutableArray *arguments = [NSMutableArray array];
  789. [self extractSQL:format argumentsList:args intoString:sql arguments:arguments];
  790. va_end(args);
  791. return [self executeUpdate:sql withArgumentsInArray:arguments];
  792. }
  793. - (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ... {
  794. va_list args;
  795. va_start(args, outErr);
  796. BOOL result = [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:args];
  797. va_end(args);
  798. return result;
  799. }
  800. - (BOOL)rollback {
  801. BOOL b = [self executeUpdate:@"rollback transaction"];
  802. if (b) {
  803. _inTransaction = NO;
  804. }
  805. return b;
  806. }
  807. - (BOOL)commit {
  808. BOOL b = [self executeUpdate:@"commit transaction"];
  809. if (b) {
  810. _inTransaction = NO;
  811. }
  812. return b;
  813. }
  814. - (BOOL)beginDeferredTransaction {
  815. BOOL b = [self executeUpdate:@"begin deferred transaction"];
  816. if (b) {
  817. _inTransaction = YES;
  818. }
  819. return b;
  820. }
  821. - (BOOL)beginTransaction {
  822. BOOL b = [self executeUpdate:@"begin exclusive transaction"];
  823. if (b) {
  824. _inTransaction = YES;
  825. }
  826. return b;
  827. }
  828. - (BOOL)inTransaction {
  829. return _inTransaction;
  830. }
  831. #if SQLITE_VERSION_NUMBER >= 3007000
  832. - (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr {
  833. // FIXME: make sure the savepoint name doesn't have a ' in it.
  834. NSParameterAssert(name);
  835. if (![self executeUpdate:[NSString stringWithFormat:@"savepoint '%@';", name]]) {
  836. if (outErr) {
  837. *outErr = [self lastError];
  838. }
  839. return NO;
  840. }
  841. return YES;
  842. }
  843. - (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr {
  844. NSParameterAssert(name);
  845. BOOL worked = [self executeUpdate:[NSString stringWithFormat:@"release savepoint '%@';", name]];
  846. if (!worked && outErr) {
  847. *outErr = [self lastError];
  848. }
  849. return worked;
  850. }
  851. - (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr {
  852. NSParameterAssert(name);
  853. BOOL worked = [self executeUpdate:[NSString stringWithFormat:@"rollback transaction to savepoint '%@';", name]];
  854. if (!worked && *outErr) {
  855. *outErr = [self lastError];
  856. }
  857. return worked;
  858. }
  859. - (NSError*)inSavePoint:(void (^)(BOOL *rollback))block {
  860. static unsigned long savePointIdx = 0;
  861. NSString *name = [NSString stringWithFormat:@"dbSavePoint%ld", savePointIdx++];
  862. BOOL shouldRollback = NO;
  863. NSError *err = 0x00;
  864. if (![self startSavePointWithName:name error:&err]) {
  865. return err;
  866. }
  867. block(&shouldRollback);
  868. if (shouldRollback) {
  869. [self rollbackToSavePointWithName:name error:&err];
  870. }
  871. else {
  872. [self releaseSavePointWithName:name error:&err];
  873. }
  874. return err;
  875. }
  876. #endif
  877. - (BOOL)shouldCacheStatements {
  878. return _shouldCacheStatements;
  879. }
  880. - (void)setShouldCacheStatements:(BOOL)value {
  881. _shouldCacheStatements = value;
  882. if (_shouldCacheStatements && !_cachedStatements) {
  883. [self setCachedStatements:[NSMutableDictionary dictionary]];
  884. }
  885. if (!_shouldCacheStatements) {
  886. [self setCachedStatements:nil];
  887. }
  888. }
  889. void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv);
  890. void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv) {
  891. #if ! __has_feature(objc_arc)
  892. void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (id)sqlite3_user_data(context);
  893. #else
  894. void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (__bridge id)sqlite3_user_data(context);
  895. #endif
  896. block(context, argc, argv);
  897. }
  898. - (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(sqlite3_context *context, int argc, sqlite3_value **argv))block {
  899. if (!_openFunctions) {
  900. _openFunctions = [NSMutableSet new];
  901. }
  902. id b = FMDBReturnAutoreleased([block copy]);
  903. [_openFunctions addObject:b];
  904. /* 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. */
  905. #if ! __has_feature(objc_arc)
  906. sqlite3_create_function([self sqliteHandle], [name UTF8String], count, SQLITE_UTF8, (void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00);
  907. #else
  908. sqlite3_create_function([self sqliteHandle], [name UTF8String], count, SQLITE_UTF8, (__bridge void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00);
  909. #endif
  910. }
  911. @end
  912. @implementation FMStatement
  913. @synthesize statement=_statement;
  914. @synthesize query=_query;
  915. @synthesize useCount=_useCount;
  916. - (void)finalize {
  917. [self close];
  918. [super finalize];
  919. }
  920. - (void)dealloc {
  921. [self close];
  922. FMDBRelease(_query);
  923. #if ! __has_feature(objc_arc)
  924. [super dealloc];
  925. #endif
  926. }
  927. - (void)close {
  928. if (_statement) {
  929. sqlite3_finalize(_statement);
  930. _statement = 0x00;
  931. }
  932. }
  933. - (void)reset {
  934. if (_statement) {
  935. sqlite3_reset(_statement);
  936. }
  937. }
  938. - (NSString*)description {
  939. return [NSString stringWithFormat:@"%@ %ld hit(s) for query %@", [super description], _useCount, _query];
  940. }
  941. @end