FMDatabase.m 46 KB

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