FMDatabase.m 33 KB

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