From 0f0c0fc78d2c90906189b6f10f94a6eacb0605a7 Mon Sep 17 00:00:00 2001 From: Marco Filetti Date: Tue, 5 Apr 2016 16:54:01 +0300 Subject: [PATCH] fmdb update --- JustUsed/Base.lproj/Main.storyboard | 4 +- JustUsed/Libraries/fmdb/FMDB.h | 5 + JustUsed/Libraries/fmdb/FMDatabase.h | 129 ++++++++--- JustUsed/Libraries/fmdb/FMDatabase.m | 207 +++++++++++------- JustUsed/Libraries/fmdb/FMDatabaseAdditions.h | 3 - JustUsed/Libraries/fmdb/FMDatabaseAdditions.m | 36 ++- JustUsed/Libraries/fmdb/FMDatabasePool.h | 4 - JustUsed/Libraries/fmdb/FMDatabasePool.m | 16 +- JustUsed/Libraries/fmdb/FMDatabaseQueue.h | 14 +- JustUsed/Libraries/fmdb/FMDatabaseQueue.m | 25 ++- JustUsed/Libraries/fmdb/FMResultSet.h | 1 - JustUsed/Libraries/fmdb/FMResultSet.m | 6 + README.md | 2 +- 13 files changed, 321 insertions(+), 131 deletions(-) diff --git a/JustUsed/Base.lproj/Main.storyboard b/JustUsed/Base.lproj/Main.storyboard index d245ab4..2b89ade 100644 --- a/JustUsed/Base.lproj/Main.storyboard +++ b/JustUsed/Base.lproj/Main.storyboard @@ -1,7 +1,7 @@ - + - + diff --git a/JustUsed/Libraries/fmdb/FMDB.h b/JustUsed/Libraries/fmdb/FMDB.h index 39e2f43..1ff5465 100755 --- a/JustUsed/Libraries/fmdb/FMDB.h +++ b/JustUsed/Libraries/fmdb/FMDB.h @@ -1,3 +1,8 @@ +#import + +FOUNDATION_EXPORT double FMDBVersionNumber; +FOUNDATION_EXPORT const unsigned char FMDBVersionString[]; + #import "FMDatabase.h" #import "FMResultSet.h" #import "FMDatabaseAdditions.h" diff --git a/JustUsed/Libraries/fmdb/FMDatabase.h b/JustUsed/Libraries/fmdb/FMDatabase.h index 9628655..7dd5f8c 100755 --- a/JustUsed/Libraries/fmdb/FMDatabase.h +++ b/JustUsed/Libraries/fmdb/FMDatabase.h @@ -1,5 +1,4 @@ #import -#import "sqlite3.h" #import "FMResultSet.h" #import "FMDatabasePool.h" @@ -73,7 +72,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary @interface FMDatabase : NSObject { - sqlite3* _db; + void* _db; NSString* _databasePath; BOOL _logsErrors; BOOL _crashOnErrors; @@ -194,7 +193,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary - (BOOL)open; -/** Opening a new database connection with flags +/** Opening a new database connection with flags and an optional virtual file system (VFS) @param flags one of the following three values, optionally combined with the `SQLITE_OPEN_NOMUTEX`, `SQLITE_OPEN_FULLMUTEX`, `SQLITE_OPEN_SHAREDCACHE`, `SQLITE_OPEN_PRIVATECACHE`, and/or `SQLITE_OPEN_URI` flags: @@ -209,7 +208,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary `SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE` The database is opened for reading and writing, and is created if it does not already exist. This is the behavior that is always used for `open` method. - + @return `YES` if successful, `NO` on error. @see [sqlite3_open_v2()](http://sqlite.org/c3ref/open.html) @@ -217,9 +216,34 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary @see close */ -#if SQLITE_VERSION_NUMBER >= 3005000 - (BOOL)openWithFlags:(int)flags; -#endif + +/** Opening a new database connection with flags and an optional virtual file system (VFS) + + @param flags one of the following three values, optionally combined with the `SQLITE_OPEN_NOMUTEX`, `SQLITE_OPEN_FULLMUTEX`, `SQLITE_OPEN_SHAREDCACHE`, `SQLITE_OPEN_PRIVATECACHE`, and/or `SQLITE_OPEN_URI` flags: + + `SQLITE_OPEN_READONLY` + + The database is opened in read-only mode. If the database does not already exist, an error is returned. + + `SQLITE_OPEN_READWRITE` + + The database is opened for reading and writing if possible, or reading only if the file is write protected by the operating system. In either case the database must already exist, otherwise an error is returned. + + `SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE` + + The database is opened for reading and writing, and is created if it does not already exist. This is the behavior that is always used for `open` method. + + @param vfsName If vfs is given the value is passed to the vfs parameter of sqlite3_open_v2. + + @return `YES` if successful, `NO` on error. + + @see [sqlite3_open_v2()](http://sqlite.org/c3ref/open.html) + @see open + @see close + */ + +- (BOOL)openWithFlags:(int)flags vfs:(NSString *)vfsName; /** Closing a database connection @@ -333,23 +357,52 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary - (BOOL)executeUpdateWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2); /** Execute single update statement - + This method executes a single SQL update statement (i.e. any SQL that does not return results, such as `UPDATE`, `INSERT`, or `DELETE`. This method employs [`sqlite3_prepare_v2`](http://sqlite.org/c3ref/prepare.html) and [`sqlite3_bind`](http://sqlite.org/c3ref/bind_blob.html) binding any `?` placeholders in the SQL with the optional list of parameters. + + The optional values provided to this method should be objects (e.g. `NSString`, `NSNumber`, `NSNull`, `NSDate`, and `NSData` objects), not fundamental data types (e.g. `int`, `long`, `NSInteger`, etc.). This method automatically handles the aforementioned object types, and all other object types will be interpreted as text values using the object's `description` method. + + @param sql The SQL to be performed, with optional `?` placeholders. + + @param arguments A `NSArray` of objects to be used when binding values to the `?` placeholders in the SQL statement. + + @return `YES` upon success; `NO` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. + + @see executeUpdate:values:error: + @see lastError + @see lastErrorCode + @see lastErrorMessage + */ + +- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments; +/** Execute single update statement + + This method executes a single SQL update statement (i.e. any SQL that does not return results, such as `UPDATE`, `INSERT`, or `DELETE`. This method employs [`sqlite3_prepare_v2`](http://sqlite.org/c3ref/prepare.html) and [`sqlite3_bind`](http://sqlite.org/c3ref/bind_blob.html) binding any `?` placeholders in the SQL with the optional list of parameters. + The optional values provided to this method should be objects (e.g. `NSString`, `NSNumber`, `NSNull`, `NSDate`, and `NSData` objects), not fundamental data types (e.g. `int`, `long`, `NSInteger`, etc.). This method automatically handles the aforementioned object types, and all other object types will be interpreted as text values using the object's `description` method. + + This is similar to ``, except that this also accepts a pointer to a `NSError` pointer, so that errors can be returned. + In Swift 2, this throws errors, as if it were defined as follows: + + `func executeUpdate(sql: String!, values: [AnyObject]!) throws -> Bool` + @param sql The SQL to be performed, with optional `?` placeholders. + + @param values A `NSArray` of objects to be used when binding values to the `?` placeholders in the SQL statement. - @param arguments A `NSArray` of objects to be used when binding values to the `?` placeholders in the SQL statement. + @param error A `NSError` object to receive any error object (if any). @return `YES` upon success; `NO` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. - + @see lastError @see lastErrorCode @see lastErrorMessage + */ -- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments; +- (BOOL)executeUpdate:(NSString*)sql values:(NSArray *)values error:(NSError * __autoreleasing *)error; /** Execute single update statement @@ -437,7 +490,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary */ -- (sqlite_int64)lastInsertRowId; +- (int64_t)lastInsertRowId; /** The number of rows changed by prior SQL statement. @@ -473,6 +526,8 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary @see FMResultSet @see [`FMResultSet next`](<[FMResultSet next]>) @see [`sqlite3_bind`](http://sqlite.org/c3ref/bind_blob.html) + + @note If you want to use this from Swift, please note that you must include `FMDatabaseVariadic.swift` in your project. Without that, you cannot use this method directly, and instead have to use methods such as ``. */ - (FMResultSet *)executeQuery:(NSString*)sql, ...; @@ -503,8 +558,6 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary There are two reasons why this distinction is important. First, the printf-style escape sequences can only be used where it is permissible to use a SQLite `?` placeholder. You can use it only for values in SQL statements, but not for table names or column names or any other non-value context. This method also cannot be used in conjunction with `pragma` statements and the like. Second, note the lack of quotation marks in the SQL. The `WHERE` clause was _not_ `WHERE name='%@'` (like you might have to do if you built a SQL statement using `NSString` method `stringWithFormat`), but rather simply `WHERE name=%@`. - @note If you want to use this from Swift, please note that you must include `FMDatabaseVariadic.swift` in your project. Without that, you cannot use this method directly, and instead have to use methods such as ``. - */ - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2); @@ -521,12 +574,42 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary @return A `` for the result set upon success; `nil` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. + @see -executeQuery:values:error: @see FMResultSet @see [`FMResultSet next`](<[FMResultSet next]>) */ - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments; +/** Execute select statement + + Executing queries returns an `` object if successful, and `nil` upon failure. Like executing updates, there is a variant that accepts an `NSError **` parameter. Otherwise you should use the `` and `` methods to determine why a query failed. + + In order to iterate through the results of your query, you use a `while()` loop. You also need to "step" (via `<[FMResultSet next]>`) from one record to the other. + + This is similar to ``, except that this also accepts a pointer to a `NSError` pointer, so that errors can be returned. + + In Swift 2, this throws errors, as if it were defined as follows: + + `func executeQuery(sql: String!, values: [AnyObject]!) throws -> FMResultSet!` + + @param sql The SELECT statement to be performed, with optional `?` placeholders. + + @param values A `NSArray` of objects to be used when binding values to the `?` placeholders in the SQL statement. + + @param error A `NSError` object to receive any error object (if any). + + @return A `` for the result set upon success; `nil` upon failure. If failed, you can call ``, ``, or `` for diagnostic information regarding the failure. + + @see FMResultSet + @see [`FMResultSet next`](<[FMResultSet next]>) + + @note When called from Swift, only use the first two parameters, `sql` and `values`. This but throws the error. + + */ + +- (FMResultSet *)executeQuery:(NSString *)sql values:(NSArray *)values error:(NSError * __autoreleasing *)error; + /** Execute select statement Executing queries returns an `` object if successful, and `nil` upon failure. Like executing updates, there is a variant that accepts an `NSError **` parameter. Otherwise you should use the `` and `` methods to determine why a query failed. @@ -662,7 +745,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary @return `YES` if success, `NO` on error. - @see http://www.sqlite-encrypt.com/develop-guide.htm + @see https://www.zetetic.net/sqlcipher/ @warning You need to have purchased the sqlite encryption extensions for this method to work. */ @@ -675,7 +758,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary @return `YES` if success, `NO` on error. - @see http://www.sqlite-encrypt.com/develop-guide.htm + @see https://www.zetetic.net/sqlcipher/ @warning You need to have purchased the sqlite encryption extensions for this method to work. */ @@ -688,7 +771,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary @return `YES` if success, `NO` on error. - @see http://www.sqlite-encrypt.com/develop-guide.htm + @see https://www.zetetic.net/sqlcipher/ @warning You need to have purchased the sqlite encryption extensions for this method to work. */ @@ -701,7 +784,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary @return `YES` if success, `NO` on error. - @see http://www.sqlite-encrypt.com/develop-guide.htm + @see https://www.zetetic.net/sqlcipher/ @warning You need to have purchased the sqlite encryption extensions for this method to work. */ @@ -727,7 +810,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary */ -- (sqlite3*)sqliteHandle; +- (void*)sqliteHandle; ///----------------------------- @@ -791,8 +874,6 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary - (NSTimeInterval)maxBusyRetryTimeInterval; -#if SQLITE_VERSION_NUMBER >= 3007000 - ///------------------ /// @name Save points ///------------------ @@ -854,8 +935,6 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary - (NSError*)inSavePoint:(void (^)(BOOL *rollback))block; -#endif - ///---------------------------- /// @name SQLite library status ///---------------------------- @@ -932,7 +1011,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary @see [sqlite3_create_function()](http://sqlite.org/c3ref/create_function.html) */ -- (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(sqlite3_context *context, int argc, sqlite3_value **argv))block; +- (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(void *context, int argc, void **argv))block; ///--------------------- @@ -1036,7 +1115,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary */ @interface FMStatement : NSObject { - sqlite3_stmt *_statement; + void *_statement; NSString *_query; long _useCount; BOOL _inUse; @@ -1059,7 +1138,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary @see [`sqlite3_stmt`](http://www.sqlite.org/c3ref/stmt.html) */ -@property (atomic, assign) sqlite3_stmt *statement; +@property (atomic, assign) void *statement; /** Indication of whether the statement is in use */ diff --git a/JustUsed/Libraries/fmdb/FMDatabase.m b/JustUsed/Libraries/fmdb/FMDatabase.m index 725c855..d33c13d 100755 --- a/JustUsed/Libraries/fmdb/FMDatabase.m +++ b/JustUsed/Libraries/fmdb/FMDatabase.m @@ -2,6 +2,12 @@ #import "unistd.h" #import +#if FMDB_SQLITE_STANDALONE +#import +#else +#import +#endif + @interface FMDatabase () - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args; @@ -67,7 +73,7 @@ - (NSString *)databasePath { } + (NSString*)FMDBUserVersion { - return @"2.5"; + return @"2.6.2"; } // returns 0x0240 for version 2.4. This makes it super easy to do things like: @@ -94,7 +100,6 @@ + (SInt32)FMDBVersion { }); - return FMDBVersionVal; } @@ -109,7 +114,7 @@ + (BOOL)isSQLiteThreadSafe { return sqlite3_threadsafe() != 0; } -- (sqlite3*)sqliteHandle { +- (void*)sqliteHandle { return _db; } @@ -134,7 +139,7 @@ - (BOOL)open { return YES; } - int err = sqlite3_open([self sqlitePath], &_db ); + int err = sqlite3_open([self sqlitePath], (sqlite3**)&_db ); if(err != SQLITE_OK) { NSLog(@"error opening!: %d", err); return NO; @@ -149,13 +154,16 @@ - (BOOL)open { return YES; } -#if SQLITE_VERSION_NUMBER >= 3005000 - (BOOL)openWithFlags:(int)flags { + return [self openWithFlags:flags vfs:nil]; +} +- (BOOL)openWithFlags:(int)flags vfs:(NSString *)vfsName { +#if SQLITE_VERSION_NUMBER >= 3005000 if (_db) { return YES; } - - int err = sqlite3_open_v2([self sqlitePath], &_db, flags, NULL /* Name of VFS module to use */); + + int err = sqlite3_open_v2([self sqlitePath], (sqlite3**)&_db, flags, [vfsName UTF8String]); if(err != SQLITE_OK) { NSLog(@"error opening!: %d", err); return NO; @@ -167,8 +175,11 @@ - (BOOL)openWithFlags:(int)flags { } return YES; -} +#else + NSLog(@"openWithFlags requires SQLite 3.5"); + return NO; #endif +} - (BOOL)close { @@ -231,7 +242,7 @@ static int FMDBDatabaseBusyHandler(void *f, int count) { NSTimeInterval delta = [NSDate timeIntervalSinceReferenceDate] - (self->_startBusyRetryTime); if (delta < [self maxBusyRetryTimeInterval]) { - int requestedSleepInMillseconds = arc4random_uniform(50) + 50; + int requestedSleepInMillseconds = (int) arc4random_uniform(50) + 50; int actualSleepInMilliseconds = sqlite3_sleep(requestedSleepInMillseconds); if (actualSleepInMilliseconds != requestedSleepInMillseconds) { NSLog(@"WARNING: Requested sleep of %i milliseconds, but SQLite returned %i. Maybe SQLite wasn't built with HAVE_USLEEP=1?", requestedSleepInMillseconds, actualSleepInMilliseconds); @@ -239,7 +250,7 @@ static int FMDBDatabaseBusyHandler(void *f, int count) { return 1; } - return 0; + return 0; } - (void)setMaxBusyRetryTimeInterval:(NSTimeInterval)timeout { @@ -274,6 +285,7 @@ - (int)busyRetryTimeout { } - (void)setBusyRetryTimeout:(int)i { +#pragma unused(i) NSLog(@"%s:%d", __FUNCTION__, __LINE__); NSLog(@"FMDB: setBusyRetryTimeout does nothing, please use setMaxBusyRetryTimeInterval:"); } @@ -368,6 +380,7 @@ - (BOOL)rekeyWithData:(NSData *)keyData { return (rc == SQLITE_OK); #else +#pragma unused(keyData) return NO; #endif } @@ -388,6 +401,7 @@ - (BOOL)setKeyWithData:(NSData *)keyData { return (rc == SQLITE_OK); #else +#pragma unused(keyData) return NO; #endif } @@ -453,15 +467,15 @@ - (void)warnInUse { - (BOOL)databaseExists { if (!_db) { - + NSLog(@"The FMDatabase %@ is not open.", self); - #ifndef NS_BLOCK_ASSERTIONS +#ifndef NS_BLOCK_ASSERTIONS if (_crashOnErrors) { NSAssert(false, @"The FMDatabase %@ is not open.", self); abort(); } - #endif +#endif return NO; } @@ -488,11 +502,11 @@ - (int)lastErrorCode { - (NSError*)errorWithMessage:(NSString*)message { NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey]; - return [NSError errorWithDomain:@"FMDatabase" code:sqlite3_errcode(_db) userInfo:errorMessage]; + return [NSError errorWithDomain:@"FMDatabase" code:sqlite3_errcode(_db) userInfo:errorMessage]; } - (NSError*)lastError { - return [self errorWithMessage:[self lastErrorMessage]]; + return [self errorWithMessage:[self lastErrorMessage]]; } #pragma mark Update information routines @@ -757,8 +771,8 @@ - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arr } if (!pStmt) { - - rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); + + rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); if (SQLITE_OK != rc) { if (_logsErrors) { @@ -789,7 +803,7 @@ - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arr // Prefix the key with a colon. NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey]; - + if (_traceExecution) { NSLog(@"%@ = %@", parameterName, [dictionaryArgs objectForKey:dictionaryKey]); } @@ -811,7 +825,7 @@ - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arr } } else { - + while (idx < queryCount) { if (arrayArgs && idx < (int)[arrayArgs count]) { @@ -867,7 +881,7 @@ - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arr [statement setUseCount:[statement useCount] + 1]; - FMDBRelease(statement); + FMDBRelease(statement); _isExecutingStatement = NO; @@ -890,7 +904,7 @@ - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... { NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]]; NSMutableArray *arguments = [NSMutableArray array]; - [self extractSQL:format argumentsList:args intoString:sql arguments:arguments]; + [self extractSQL:format argumentsList:args intoString:sql arguments:arguments]; va_end(args); @@ -901,6 +915,14 @@ - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)ar return [self executeQuery:sql withArgumentsInArray:arguments orDictionary:nil orVAList:nil]; } +- (FMResultSet *)executeQuery:(NSString *)sql values:(NSArray *)values error:(NSError * __autoreleasing *)error { + FMResultSet *rs = [self executeQuery:sql withArgumentsInArray:values orDictionary:nil orVAList:nil]; + if (!rs && error) { + *error = [self lastError]; + } + return rs; +} + - (FMResultSet *)executeQuery:(NSString*)sql withVAList:(va_list)args { return [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args]; } @@ -949,12 +971,12 @@ - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArra abort(); } - sqlite3_finalize(pStmt); - if (outErr) { *outErr = [self errorWithMessage:[NSString stringWithUTF8String:sqlite3_errmsg(_db)]]; } + sqlite3_finalize(pStmt); + _isExecutingStatement = NO; return NO; } @@ -988,7 +1010,14 @@ - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArra idx++; } else { - NSLog(@"Could not find index for %@", dictionaryKey); + NSString *message = [NSString stringWithFormat:@"Could not find index for %@", dictionaryKey]; + + if (_logsErrors) { + NSLog(@"%@", message); + } + if (outErr) { + *outErr = [self errorWithMessage:message]; + } } } } @@ -1024,7 +1053,14 @@ - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArra if (idx != queryCount) { - NSLog(@"Error: the bind count (%d) is not correct for the # of variables in the query (%d) (%@) (executeUpdate)", idx, queryCount, sql); + NSString *message = [NSString stringWithFormat:@"Error: the bind count (%d) is not correct for the # of variables in the query (%d) (%@) (executeUpdate)", idx, queryCount, sql]; + if (_logsErrors) { + NSLog(@"%@", message); + } + if (outErr) { + *outErr = [self errorWithMessage:message]; + } + sqlite3_finalize(pStmt); _isExecutingStatement = NO; return NO; @@ -1039,29 +1075,41 @@ - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArra if (SQLITE_DONE == rc) { // all is well, let's return. } - else if (SQLITE_ERROR == rc) { + else if (rc == SQLITE_ROW) { + NSString *message = [NSString stringWithFormat:@"A executeUpdate is being called with a query string '%@'", sql]; if (_logsErrors) { - NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db)); + NSLog(@"%@", message); NSLog(@"DB Query: %@", sql); } - } - else if (SQLITE_MISUSE == rc) { - // uh oh. - if (_logsErrors) { - NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db)); - NSLog(@"DB Query: %@", sql); + if (outErr) { + *outErr = [self errorWithMessage:message]; } } else { - // wtf? - if (_logsErrors) { - NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db)); - NSLog(@"DB Query: %@", sql); + if (outErr) { + *outErr = [self errorWithMessage:[NSString stringWithUTF8String:sqlite3_errmsg(_db)]]; + } + + if (SQLITE_ERROR == rc) { + if (_logsErrors) { + NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db)); + NSLog(@"DB Query: %@", sql); + } + } + else if (SQLITE_MISUSE == rc) { + // uh oh. + if (_logsErrors) { + NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db)); + NSLog(@"DB Query: %@", sql); + } + } + else { + // wtf? + if (_logsErrors) { + NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db)); + NSLog(@"DB Query: %@", sql); + } } - } - - if (rc == SQLITE_ROW) { - NSAssert(NO, @"A executeUpdate is being called with a query string '%@'", sql); } if (_shouldCacheStatements && !cachedStmt) { @@ -1113,6 +1161,10 @@ - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments { return [self executeUpdate:sql error:nil withArgumentsInArray:arguments orDictionary:nil orVAList:nil]; } +- (BOOL)executeUpdate:(NSString*)sql values:(NSArray *)values error:(NSError * __autoreleasing *)error { + return [self executeUpdate:sql error:error withArgumentsInArray:values orDictionary:nil orVAList:nil]; +} + - (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments { return [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:arguments orVAList:nil]; } @@ -1128,7 +1180,7 @@ - (BOOL)executeUpdateWithFormat:(NSString*)format, ... { NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]]; NSMutableArray *arguments = [NSMutableArray array]; - [self extractSQL:format argumentsList:args intoString:sql arguments:arguments]; + [self extractSQL:format argumentsList:args intoString:sql arguments:arguments]; va_end(args); @@ -1171,7 +1223,7 @@ - (BOOL)executeStatements:(NSString *)sql withResultBlock:(FMDBExecuteStatements NSLog(@"Error inserting batch: %s", errmsg); sqlite3_free(errmsg); } - + return (rc == SQLITE_OK); } @@ -1247,59 +1299,54 @@ - (BOOL)inTransaction { return _inTransaction; } -#if SQLITE_VERSION_NUMBER >= 3007000 - static NSString *FMDBEscapeSavePointName(NSString *savepointName) { return [savepointName stringByReplacingOccurrencesOfString:@"'" withString:@"''"]; } - (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr { - +#if SQLITE_VERSION_NUMBER >= 3007000 NSParameterAssert(name); NSString *sql = [NSString stringWithFormat:@"savepoint '%@';", FMDBEscapeSavePointName(name)]; - if (![self executeUpdate:sql]) { - - if (outErr) { - *outErr = [self lastError]; - } - - return NO; - } - - return YES; + return [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:nil]; +#else + NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); + if (self.logsErrors) NSLog(@"%@", errorMessage); + return NO; +#endif } - (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr { - +#if SQLITE_VERSION_NUMBER >= 3007000 NSParameterAssert(name); NSString *sql = [NSString stringWithFormat:@"release savepoint '%@';", FMDBEscapeSavePointName(name)]; - BOOL worked = [self executeUpdate:sql]; - - if (!worked && outErr) { - *outErr = [self lastError]; - } - - return worked; + + return [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:nil]; +#else + NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); + if (self.logsErrors) NSLog(@"%@", errorMessage); + return NO; +#endif } - (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr { - +#if SQLITE_VERSION_NUMBER >= 3007000 NSParameterAssert(name); NSString *sql = [NSString stringWithFormat:@"rollback transaction to savepoint '%@';", FMDBEscapeSavePointName(name)]; - BOOL worked = [self executeUpdate:sql]; - - if (!worked && outErr) { - *outErr = [self lastError]; - } - - return worked; + + return [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:nil]; +#else + NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); + if (self.logsErrors) NSLog(@"%@", errorMessage); + return NO; +#endif } - (NSError*)inSavePoint:(void (^)(BOOL *rollback))block { +#if SQLITE_VERSION_NUMBER >= 3007000 static unsigned long savePointIdx = 0; NSString *name = [NSString stringWithFormat:@"dbSavePoint%ld", savePointIdx++]; @@ -1312,7 +1359,9 @@ - (NSError*)inSavePoint:(void (^)(BOOL *rollback))block { return err; } - block(&shouldRollback); + if (block) { + block(&shouldRollback); + } if (shouldRollback) { // We need to rollback and release this savepoint to remove it @@ -1321,9 +1370,13 @@ - (NSError*)inSavePoint:(void (^)(BOOL *rollback))block { [self releaseSavePointWithName:name error:&err]; return err; +#else + NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); + if (self.logsErrors) NSLog(@"%@", errorMessage); + return [NSError errorWithDomain:@"FMDatabase" code:0 userInfo:@{NSLocalizedDescriptionKey : errorMessage}]; +#endif } -#endif #pragma mark Cache statements @@ -1353,11 +1406,13 @@ void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3 #else void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (__bridge id)sqlite3_user_data(context); #endif - block(context, argc, argv); + if (block) { + block(context, argc, argv); + } } -- (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(sqlite3_context *context, int argc, sqlite3_value **argv))block { +- (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(void *context, int argc, void **argv))block { if (!_openFunctions) { _openFunctions = [NSMutableSet new]; diff --git a/JustUsed/Libraries/fmdb/FMDatabaseAdditions.h b/JustUsed/Libraries/fmdb/FMDatabaseAdditions.h index 85bb277..9dd0b62 100755 --- a/JustUsed/Libraries/fmdb/FMDatabaseAdditions.h +++ b/JustUsed/Libraries/fmdb/FMDatabaseAdditions.h @@ -209,8 +209,6 @@ - (BOOL)validateSQL:(NSString*)sql error:(NSError**)error; -#if SQLITE_VERSION_NUMBER >= 3007017 - ///----------------------------------- /// @name Application identifier tasks ///----------------------------------- @@ -252,7 +250,6 @@ */ - (void)setApplicationIDString:(NSString*)string; -#endif #endif diff --git a/JustUsed/Libraries/fmdb/FMDatabaseAdditions.m b/JustUsed/Libraries/fmdb/FMDatabaseAdditions.m index 4ab35fa..61fa747 100755 --- a/JustUsed/Libraries/fmdb/FMDatabaseAdditions.m +++ b/JustUsed/Libraries/fmdb/FMDatabaseAdditions.m @@ -10,6 +10,12 @@ #import "FMDatabaseAdditions.h" #import "TargetConditionals.h" +#if FMDB_SQLITE_STANDALONE +#import +#else +#import +#endif + @interface FMDatabase (PrivateStuff) - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args; @end @@ -119,10 +125,9 @@ - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName } -#if SQLITE_VERSION_NUMBER >= 3007017 - (uint32_t)applicationID { - +#if SQLITE_VERSION_NUMBER >= 3007017 uint32_t r = 0; FMResultSet *rs = [self executeQuery:@"pragma application_id"]; @@ -134,18 +139,30 @@ - (uint32_t)applicationID { [rs close]; return r; +#else + NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil); + if (self.logsErrors) NSLog(@"%@", errorMessage); + return 0; +#endif } - (void)setApplicationID:(uint32_t)appID { +#if SQLITE_VERSION_NUMBER >= 3007017 NSString *query = [NSString stringWithFormat:@"pragma application_id=%d", appID]; FMResultSet *rs = [self executeQuery:query]; [rs next]; [rs close]; +#else + NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil); + if (self.logsErrors) NSLog(@"%@", errorMessage); +#endif } #if TARGET_OS_MAC && !TARGET_OS_IPHONE + - (NSString*)applicationIDString { +#if SQLITE_VERSION_NUMBER >= 3007017 NSString *s = NSFileTypeForHFSTypeCode([self applicationID]); assert([s length] == 6); @@ -154,20 +171,25 @@ - (NSString*)applicationIDString { return s; - +#else + NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil); + if (self.logsErrors) NSLog(@"%@", errorMessage); + return nil; +#endif } - (void)setApplicationIDString:(NSString*)s { - +#if SQLITE_VERSION_NUMBER >= 3007017 if ([s length] != 4) { NSLog(@"setApplicationIDString: string passed is not exactly 4 chars long. (was %ld)", [s length]); } [self setApplicationID:NSHFSTypeCodeFromFileType([NSString stringWithFormat:@"'%@'", s])]; -} - - +#else + NSString *errorMessage = NSLocalizedString(@"Application ID functions require SQLite 3.7.17", nil); + if (self.logsErrors) NSLog(@"%@", errorMessage); #endif +} #endif diff --git a/JustUsed/Libraries/fmdb/FMDatabasePool.h b/JustUsed/Libraries/fmdb/FMDatabasePool.h index 692b8ae..1915858 100755 --- a/JustUsed/Libraries/fmdb/FMDatabasePool.h +++ b/JustUsed/Libraries/fmdb/FMDatabasePool.h @@ -7,7 +7,6 @@ // #import -#import "sqlite3.h" @class FMDatabase; @@ -156,8 +155,6 @@ - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; -#if SQLITE_VERSION_NUMBER >= 3007000 - /** Synchronously perform database operations in pool using save point. @param block The code to be run on the `FMDatabasePool` pool. @@ -168,7 +165,6 @@ */ - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; -#endif @end diff --git a/JustUsed/Libraries/fmdb/FMDatabasePool.m b/JustUsed/Libraries/fmdb/FMDatabasePool.m index 010e292..e8e52cb 100755 --- a/JustUsed/Libraries/fmdb/FMDatabasePool.m +++ b/JustUsed/Libraries/fmdb/FMDatabasePool.m @@ -6,6 +6,12 @@ // Copyright 2011 Flying Meat Inc. All rights reserved. // +#if FMDB_SQLITE_STANDALONE +#import +#else +#import +#endif + #import "FMDatabasePool.h" #import "FMDatabase.h" @@ -238,9 +244,9 @@ - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { [self beginTransaction:NO withBlock:block]; } -#if SQLITE_VERSION_NUMBER >= 3007000 + - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { - +#if SQLITE_VERSION_NUMBER >= 3007000 static unsigned long savePointIdx = 0; NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; @@ -267,7 +273,11 @@ - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { [self pushDatabaseBackInPool:db]; return err; -} +#else + NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); + if (self.logsErrors) NSLog(@"%@", errorMessage); + return [NSError errorWithDomain:@"FMDatabase" code:0 userInfo:@{NSLocalizedDescriptionKey : errorMessage}]; #endif +} @end diff --git a/JustUsed/Libraries/fmdb/FMDatabaseQueue.h b/JustUsed/Libraries/fmdb/FMDatabaseQueue.h index 34c0750..ae45b65 100755 --- a/JustUsed/Libraries/fmdb/FMDatabaseQueue.h +++ b/JustUsed/Libraries/fmdb/FMDatabaseQueue.h @@ -7,7 +7,6 @@ // #import -#import "sqlite3.h" @class FMDatabase; @@ -117,6 +116,17 @@ - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags; +/** Create queue using path and specified flags. + + @param aPath The file path of the database. + @param openFlags Flags passed to the openWithFlags method of the database + @param vfsName The name of a custom virtual file system + + @return The `FMDatabaseQueue` object. `nil` on error. + */ + +- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName; + /** Returns the Class of 'FMDatabase' subclass, that will be used to instantiate database object. Subclasses can override this method to return specified Class of 'FMDatabase' subclass. @@ -164,11 +174,9 @@ @param block The code to be run on the queue of `FMDatabaseQueue` */ -#if SQLITE_VERSION_NUMBER >= 3007000 // NOTE: you can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. // If you need to nest, use FMDatabase's startSavePointWithName:error: instead. - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; -#endif @end diff --git a/JustUsed/Libraries/fmdb/FMDatabaseQueue.m b/JustUsed/Libraries/fmdb/FMDatabaseQueue.m index ccf31fb..c877a34 100755 --- a/JustUsed/Libraries/fmdb/FMDatabaseQueue.m +++ b/JustUsed/Libraries/fmdb/FMDatabaseQueue.m @@ -9,6 +9,12 @@ #import "FMDatabaseQueue.h" #import "FMDatabase.h" +#if FMDB_SQLITE_STANDALONE +#import +#else +#import +#endif + /* Note: we call [self retain]; before using dispatch_sync, just incase @@ -51,7 +57,7 @@ + (Class)databaseClass { return [FMDatabase class]; } -- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags { +- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName { self = [super init]; @@ -61,7 +67,7 @@ - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags { FMDBRetain(_db); #if SQLITE_VERSION_NUMBER >= 3005000 - BOOL success = [_db openWithFlags:openFlags]; + BOOL success = [_db openWithFlags:openFlags vfs:vfsName]; #else BOOL success = [_db open]; #endif @@ -81,10 +87,14 @@ - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags { return self; } +- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags { + return [self initWithPath:aPath flags:openFlags vfs:nil]; +} + - (instancetype)initWithPath:(NSString*)aPath { // default flags for sqlite3_open - return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE]; + return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE vfs:nil]; } - (instancetype)init { @@ -200,9 +210,8 @@ - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { [self beginTransaction:NO withBlock:block]; } -#if SQLITE_VERSION_NUMBER >= 3007000 - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { - +#if SQLITE_VERSION_NUMBER >= 3007000 static unsigned long savePointIdx = 0; __block NSError *err = 0x00; FMDBRetain(self); @@ -226,7 +235,11 @@ - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { }); FMDBRelease(self); return err; -} +#else + NSString *errorMessage = NSLocalizedString(@"Save point functions require SQLite 3.7", nil); + if (self.logsErrors) NSLog(@"%@", errorMessage); + return [NSError errorWithDomain:@"FMDatabase" code:0 userInfo:@{NSLocalizedDescriptionKey : errorMessage}]; #endif +} @end diff --git a/JustUsed/Libraries/fmdb/FMResultSet.h b/JustUsed/Libraries/fmdb/FMResultSet.h index 65250f0..af0433b 100755 --- a/JustUsed/Libraries/fmdb/FMResultSet.h +++ b/JustUsed/Libraries/fmdb/FMResultSet.h @@ -1,5 +1,4 @@ #import -#import "sqlite3.h" #ifndef __has_feature // Optional. #define __has_feature(x) 0 // Compatibility with non-clang compilers. diff --git a/JustUsed/Libraries/fmdb/FMResultSet.m b/JustUsed/Libraries/fmdb/FMResultSet.m index ca7e166..cfc51e1 100755 --- a/JustUsed/Libraries/fmdb/FMResultSet.m +++ b/JustUsed/Libraries/fmdb/FMResultSet.m @@ -2,6 +2,12 @@ #import "FMDatabase.h" #import "unistd.h" +#if FMDB_SQLITE_STANDALONE +#import +#else +#import +#endif + @interface FMDatabase () - (void)resultSetDidClose:(FMResultSet *)resultSet; @end diff --git a/README.md b/README.md index 58ec276..60e47d9 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,6 @@ The following GitHub projects are incorporated into JustUsed (no additional down - [Swifty JSON version 2.3.1](https://github.com/SwiftyJSON/SwiftyJSON/releases/tag/2.3.1) - To easily parse and manage JSON objects pushed to DiMe. -- [FMDB version 2.5](https://github.com/ccgus/fmdb/releases/tag/v2.5) - to track Safari's SQLite database +- [FMDB version 2.6.2](https://github.com/ccgus/fmdb/releases/tag/2.6.2) - to track Safari's SQLite database - [XCGLogger version 3.3](https://github.com/DaveWoodCom/XCGLogger/releases/tag/Version_3.3) - To output logs to terminal and files