diff options
author | Robert M. Ryan <robert.ryan@mindspring.com> | 2015-10-30 06:22:06 +0300 |
---|---|---|
committer | Robert M. Ryan <robert.ryan@mindspring.com> | 2015-10-30 06:22:06 +0300 |
commit | d2e1b514127547d7c5a62c5aeb25dd24b18e9501 (patch) | |
tree | 9d4892734d88d75c6b4e3134dec1403fb93bf62b | |
parent | b6f7a7d9314f5a3f7df28eb3061ecc3c33559cc7 (diff) |
Add renditions of executeQuery and executeUpdate that throw errors in Swift
These renditions are `executeQuery:values:error:` and `executeUpdate:values:error:`.
Updated README, which has Swift 2 example, accordingly.
-rw-r--r-- | CHANGES_AND_TODO_LIST.txt | 3 | ||||
-rw-r--r-- | README.markdown | 23 | ||||
-rw-r--r-- | Tests/Tests-Info.plist | 2 | ||||
-rw-r--r-- | fmdb.xcodeproj/project.pbxproj | 15 | ||||
-rw-r--r-- | fmdb.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme | 13 | ||||
-rw-r--r-- | src/fmdb/FMDatabase.h | 67 | ||||
-rw-r--r-- | src/fmdb/FMDatabase.m | 12 |
7 files changed, 105 insertions, 30 deletions
diff --git a/CHANGES_AND_TODO_LIST.txt b/CHANGES_AND_TODO_LIST.txt index 44db745..c6a375d 100644 --- a/CHANGES_AND_TODO_LIST.txt +++ b/CHANGES_AND_TODO_LIST.txt @@ -3,6 +3,9 @@ Zip, nada, zilch. Got any ideas? If you would like to contribute some code- awesome! I just ask that you make it conform to the coding conventions already set in here, and to add a couple of tests for your new code to fmdb.m. And of course, the code should be of general use to more than just a couple of folks. Send your patches to gus@flyingmeat.com. +2015.10.29 + Added renditions of `executeUpdate:values:error:` and `executeQuery:values:error:`, which in Swift 2 throw errors. + 2015.01.23 Added Swift renditions of the variadic methods of FMDatabaseAdditions. diff --git a/README.markdown b/README.markdown index 934579d..1ec0778 100644 --- a/README.markdown +++ b/README.markdown @@ -278,7 +278,7 @@ To do this, you must: #import "FMDB.h" ``` -4. Optionally, copy the `FMDatabaseVariadic.swift` from the "src/extra/Swift Extensions" folder into your project. This allows you to use `executeUpdate` and `executeQuery` with variadic parameters, rather than the `withArgumentsInArray` rendition. +4. Use the variations of `executeQuery` and `executeUpdate` with the `sql` and `values` parameters with `try` pattern, as shown below. These renditions of `executeQuery` and `executeUpdate` both `throw` errors in true Swift 2 fashion. If you do the above, you can then write Swift code that uses FMDatabase. For example: @@ -293,27 +293,20 @@ if !database.open() { return } -if !database.executeUpdate("create table test(x text, y text, z text)", withArgumentsInArray: nil) { - print("create table failed: \(database.lastErrorMessage())") -} - -if !database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", withArgumentsInArray: ["a", "b", "c"]) { - print("insert 1 table failed: \(database.lastErrorMessage())") -} - -if !database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", withArgumentsInArray: ["e", "f", "g"]) { - print("insert 2 table failed: \(database.lastErrorMessage())") -} +do { + try database.executeUpdate("create table test(x text, y text, z text)", values: nil) + try database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", values: ["a", "b", "c"]) + try database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", values: ["e", "f", "g"]) -if let rs = database.executeQuery("select x, y, z from test", withArgumentsInArray: nil) { + let rs = try database.executeQuery("select x, y, z from test", values: nil) while rs.next() { let x = rs.stringForColumn("x") let y = rs.stringForColumn("y") let z = rs.stringForColumn("z") print("x = \(x); y = \(y); z = \(z)") } -} else { - print("select failed: \(database.lastErrorMessage())") +} catch let error as NSError { + print("failed: \(error.localizedDescription)") } database.close() diff --git a/Tests/Tests-Info.plist b/Tests/Tests-Info.plist index 30e7ee2..169b6f7 100644 --- a/Tests/Tests-Info.plist +++ b/Tests/Tests-Info.plist @@ -7,7 +7,7 @@ <key>CFBundleExecutable</key> <string>${EXECUTABLE_NAME}</string> <key>CFBundleIdentifier</key> - <string>me.grahamdennis.${PRODUCT_NAME:rfc1034identifier}</string> + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundlePackageType</key> diff --git a/fmdb.xcodeproj/project.pbxproj b/fmdb.xcodeproj/project.pbxproj index 37772d8..89d0700 100644 --- a/fmdb.xcodeproj/project.pbxproj +++ b/fmdb.xcodeproj/project.pbxproj @@ -436,7 +436,7 @@ 08FB7793FE84155DC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0610; + LastUpgradeCheck = 0710; TargetAttributes = { BF5D041518416BB2008C5AA9 = { TestTargetID = EE4290EE12B42F870088BD94; @@ -608,7 +608,9 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = c99; + GCC_NO_COMMON_BLOCKS = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; @@ -620,7 +622,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx10.9; + SDKROOT = macosx; }; name = Debug; }; @@ -636,13 +638,14 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = c99; + GCC_NO_COMMON_BLOCKS = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - SDKROOT = macosx10.9; + SDKROOT = macosx; }; name = Release; }; @@ -737,8 +740,9 @@ INFOPLIST_FILE = "Tests/Tests-Info.plist"; MACOSX_DEPLOYMENT_TARGET = 10.8; ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "me.grahamdennis.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx10.9; + SDKROOT = macosx; WRAPPER_EXTENSION = xctest; }; name = Debug; @@ -772,8 +776,9 @@ GCC_WARN_UNUSED_FUNCTION = YES; INFOPLIST_FILE = "Tests/Tests-Info.plist"; MACOSX_DEPLOYMENT_TARGET = 10.8; + PRODUCT_BUNDLE_IDENTIFIER = "me.grahamdennis.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx10.9; + SDKROOT = macosx; WRAPPER_EXTENSION = xctest; }; name = Release; diff --git a/fmdb.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/fmdb.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index 23ed8cf..754abff 100644 --- a/fmdb.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/fmdb.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0630" + LastUpgradeVersion = "0710" version = "1.3"> <BuildAction parallelizeBuildables = "YES" @@ -23,10 +23,10 @@ </BuildActionEntries> </BuildAction> <TestAction + buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES" - buildConfiguration = "Debug"> + shouldUseLaunchSchemeArgsEnv = "YES"> <Testables> <TestableReference skipped = "NO"> @@ -48,15 +48,18 @@ ReferencedContainer = "container:fmdb.xcodeproj"> </BuildableReference> </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> </TestAction> <LaunchAction + buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" launchStyle = "0" useCustomWorkingDirectory = "NO" - buildConfiguration = "Debug" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" + debugServiceExtension = "internal" allowLocationSimulation = "YES"> <MacroExpansion> <BuildableReference @@ -71,10 +74,10 @@ </AdditionalOptions> </LaunchAction> <ProfileAction + buildConfiguration = "Release" shouldUseLaunchSchemeArgsEnv = "YES" savedToolIdentifier = "" useCustomWorkingDirectory = "NO" - buildConfiguration = "Release" debugDocumentVersioning = "YES"> <MacroExpansion> <BuildableReference diff --git a/src/fmdb/FMDatabase.h b/src/fmdb/FMDatabase.h index 1aec77d..213d222 100644 --- a/src/fmdb/FMDatabase.h +++ b/src/fmdb/FMDatabase.h @@ -336,23 +336,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 `<lastError>`, `<lastErrorCode>`, or `<lastErrorMessage>` 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 `<executeUpdate:withArgumentsInArray:>`, 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 `<lastError>`, `<lastErrorCode>`, or `<lastErrorMessage>` 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 @@ -524,6 +553,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary @return A `<FMResultSet>` for the result set upon success; `nil` upon failure. If failed, you can call `<lastError>`, `<lastErrorCode>`, or `<lastErrorMessage>` for diagnostic information regarding the failure. + @see -executeQuery:values:error: @see FMResultSet @see [`FMResultSet next`](<[FMResultSet next]>) */ @@ -531,6 +561,35 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments; /** Execute select statement + + Executing queries returns an `<FMResultSet>` object if successful, and `nil` upon failure. Like executing updates, there is a variant that accepts an `NSError **` parameter. Otherwise you should use the `<lastErrorMessage>` and `<lastErrorMessage>` 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 `<executeQuery:withArgumentsInArray:>`, 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 `<FMResultSet>` for the result set upon success; `nil` upon failure. If failed, you can call `<lastError>`, `<lastErrorCode>`, or `<lastErrorMessage>` 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 `<FMResultSet>` object if successful, and `nil` upon failure. Like executing updates, there is a variant that accepts an `NSError **` parameter. Otherwise you should use the `<lastErrorMessage>` and `<lastErrorMessage>` methods to determine why a query failed. diff --git a/src/fmdb/FMDatabase.m b/src/fmdb/FMDatabase.m index 40e3b06..e9757a6 100644 --- a/src/fmdb/FMDatabase.m +++ b/src/fmdb/FMDatabase.m @@ -907,6 +907,14 @@ static int FMDBDatabaseBusyHandler(void *f, int count) { 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]; } @@ -1119,6 +1127,10 @@ static int FMDBDatabaseBusyHandler(void *f, int count) { 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]; } |