FMDatabase.m 41 KB

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