Skip to content

Commit

Permalink
Add renditions of executeQuery and executeUpdate that throw errors in…
Browse files Browse the repository at this point in the history
… Swift

These renditions are `executeQuery:values:error:` and `executeUpdate:values:error:`.

Updated README, which has Swift 2 example, accordingly.
  • Loading branch information
robertmryan committed Oct 30, 2015
1 parent b6f7a7d commit d2e1b51
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 30 deletions.
3 changes: 3 additions & 0 deletions CHANGES_AND_TODO_LIST.txt
Expand Up @@ -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.

Expand Down
23 changes: 8 additions & 15 deletions README.markdown
Expand Up @@ -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:

Expand All @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion Tests/Tests-Info.plist
Expand Up @@ -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>
Expand Down
15 changes: 10 additions & 5 deletions fmdb.xcodeproj/project.pbxproj
Expand Up @@ -436,7 +436,7 @@
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0610;
LastUpgradeCheck = 0710;
TargetAttributes = {
BF5D041518416BB2008C5AA9 = {
TestTargetID = EE4290EE12B42F870088BD94;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
};
Expand All @@ -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;
};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
13 changes: 8 additions & 5 deletions 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"
Expand All @@ -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">
Expand All @@ -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
Expand All @@ -71,10 +74,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
Expand Down
67 changes: 63 additions & 4 deletions src/fmdb/FMDatabase.h
Expand Up @@ -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
Expand Down Expand Up @@ -524,12 +553,42 @@ 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]>)
*/

- (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.
Expand Down
12 changes: 12 additions & 0 deletions src/fmdb/FMDatabase.m
Expand Up @@ -907,6 +907,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];
}
Expand Down Expand Up @@ -1119,6 +1127,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];
}
Expand Down

0 comments on commit d2e1b51

Please sign in to comment.