FMDatabase.m 44 KB

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