Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ccgus/fmdb.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Ryan <robert.ryan@mindspring.com>2017-05-26 17:23:03 +0300
committerGitHub <noreply@github.com>2017-05-26 17:23:03 +0300
commitc1653c076afc173f5015846bff92a0db19ca94e5 (patch)
treeebe09096cd7970e8e3356f01b965e121922d1d09
parent08d74004ba1934b9121296749228b456e70506b6 (diff)
parent9756de3da1989fde53e9121b3cfa6e2e47041366 (diff)
Merge pull request #584 from robertmryan/master
2.7 - Audit for nullability
-rw-r--r--FMDB.podspec2
-rw-r--r--README.markdown121
-rw-r--r--Tests/FMDatabaseAdditionsTests.m19
-rw-r--r--Tests/FMDatabasePoolTests.m99
-rw-r--r--Tests/FMDatabaseQueueTests.m84
-rw-r--r--Tests/FMDatabaseTests.m400
-rw-r--r--fmdb.xcodeproj/project.pbxproj46
-rw-r--r--fmdb.xcodeproj/xcshareddata/xcschemes/FMDB MacOS.xcscheme2
-rw-r--r--fmdb.xcodeproj/xcshareddata/xcschemes/FMDB iOS.xcscheme2
-rw-r--r--src/extra/Swift extensions/FMDatabaseAdditionsVariadic.swift117
-rw-r--r--src/extra/Swift extensions/FMDatabaseVariadic.swift38
-rw-r--r--src/extra/fts3/FMDatabase+FTS3.m2
-rw-r--r--src/extra/fts3/FMTokenizers.h8
-rw-r--r--src/fmdb/FMDatabase.h253
-rw-r--r--src/fmdb/FMDatabase.m152
-rw-r--r--src/fmdb/FMDatabaseAdditions.h54
-rw-r--r--src/fmdb/FMDatabaseAdditions.m7
-rwxr-xr-xsrc/fmdb/FMDatabasePool.h109
-rwxr-xr-xsrc/fmdb/FMDatabasePool.m37
-rwxr-xr-xsrc/fmdb/FMDatabaseQueue.h82
-rwxr-xr-xsrc/fmdb/FMDatabaseQueue.m50
-rw-r--r--src/fmdb/FMResultSet.h81
-rw-r--r--src/fmdb/FMResultSet.m42
-rw-r--r--src/fmdb/Info.plist2
-rw-r--r--src/sample/main.m4
25 files changed, 1224 insertions, 589 deletions
diff --git a/FMDB.podspec b/FMDB.podspec
index c133887..2bde797 100644
--- a/FMDB.podspec
+++ b/FMDB.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'FMDB'
- s.version = '2.6.2'
+ s.version = '2.7'
s.summary = 'A Cocoa / Objective-C wrapper around SQLite.'
s.homepage = 'https://github.com/ccgus/fmdb'
s.license = 'MIT'
diff --git a/README.markdown b/README.markdown
index 823ec0a..7cf5288 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,4 +1,4 @@
-# FMDB v2.6.2
+# FMDB v2.7
This is an Objective-C wrapper around SQLite: http://sqlite.org/
@@ -36,6 +36,102 @@ http://ccgus.github.io/fmdb/html/index.html
## Automatic Reference Counting (ARC) or Manual Memory Management?
You can use either style in your Cocoa project. FMDB will figure out which you are using at compile time and do the right thing.
+## What's New in FMDB 2.7
+
+FMDB 2.7 attempts to support a more natural interface. This represents a fairly significant change for Swift developers (audited for nullability; shifted to properties in external interfaces where possible rather than methods; etc.). For Objective-C developers, this should be a fairly seamless transition (unless you were using the ivars that were previously exposed in the public interface, which you shouldn't have been doing, anyway!).
+
+### Nullability and Swift Optionals
+
+FMDB 2.7 is largely the same as prior versions, but has been audited for nullability. For Objective-C users, this simply means that if you perform a static analysis of your FMDB-based project, you may receive more meaningful warnings as you review your project, but there are likely to be few, if any, changes necessary in your code.
+
+For Swift users, this nullability audit results in changes that are not entirely backward compatible with FMDB 2.6, but is a little more Swifty. Before FMDB was audited for nullability, Swift was forced to defensively assume that variables were optional, but the library now more accurately knows which properties and method parameters are optional, and which are not.
+
+This means, though, that Swift code written for FMDB 2.7 may require changes. For example, consider the following Swift 3 code written for FMDB 2.6:
+```swift
+
+guard let queue = FMDatabaseQueue(path: fileURL.path) else {
+ print("Unable to create FMDatabaseQueue")
+ return
+}
+
+queue.inTransaction { db, rollback in
+ do {
+ guard let db == db else {
+ // handle error here
+ return
+ }
+
+ try db.executeUpdate("INSERT INTO foo (bar) VALUES (?)", values: [1])
+ try db.executeUpdate("INSERT INTO foo (bar) VALUES (?)", values: [2])
+ } catch {
+ rollback?.pointee = true
+ }
+}
+```
+
+Because FMDB 2.6 was not audited for nullability, Swift inferred that `db` and `rollback` were optionals. But, now, in FMDB 2.7, Swift now knows that, for example, neither `db` nor `rollback` above can be `nil`, so they are no longer optionals. Thus it becomes:
+
+```swift
+
+let queue = FMDatabaseQueue(url: fileURL)
+
+queue.inTransaction { db, rollback in
+ do {
+ try db.executeUpdate("INSERT INTO foo (bar) VALUES (?)", values: [1])
+ try db.executeUpdate("INSERT INTO foo (bar) VALUES (?)", values: [2])
+ } catch {
+ rollback.pointee = true
+ }
+}
+```
+
+### Custom Functions
+
+In the past, when writing custom functions, you would have to generally include your own `@autoreleasepool` block to avoid problems when writing functions that scanned through a large table. Now, FMDB will automatically wrap it in an autorelease pool, so you don't have to.
+
+Also, in the past, when retrieving the values passed to the function, you had to drop down to the SQLite C API and include your own `sqlite3_value_XXX` calls. There are now `FMDatabase` methods, `valueInt`, `valueString`, etc., so you can stay within Swift and/or Objective-C, without needing to call the C functions yourself. Likewise, when specifying the return values, you no longer need to call `sqlite3_result_XXX` C API, but rather you can use `FMDatabase` methods, `resultInt`, `resultString`, etc. There is a new `enum` for `valueType` called `SqliteValueType`, which can be used for checking the type of parameter passed to the custom function.
+
+Thus, you can do something like (in Swift 3):
+
+```swift
+db.makeFunctionNamed("RemoveDiacritics", arguments: 1) { context, argc, argv in
+ guard db.valueType(argv[0]) == .text || db.valueType(argv[0]) == .null else {
+ db.resultError("Expected string parameter", context: context)
+ return
+ }
+
+ if let string = db.valueString(argv[0])?.folding(options: .diacriticInsensitive, locale: nil) {
+ db.resultString(string, context: context)
+ } else {
+ db.resultNull(context: context)
+ }
+}
+```
+
+And you can then use that function in your SQL (in this case, matching both "Jose" and "José"):
+
+```sql
+SELECT * FROM employees WHERE RemoveDiacritics(first_name) LIKE 'jose'
+```
+
+Note, the method `makeFunctionNamed:maximumArguments:withBlock:` has been renamed to `makeFunctionNamed:arguments:block:`, to more accurately reflect the functional intent of the second parameter.
+
+### API Changes
+
+In addition to the `makeFunctionNamed` noted above, there are a few other API changes. Specifically,
+
+ - To become consistent with the rest of the API, the methods `objectForColumnName` and `UTF8StringForColumnName` have been renamed to `objectForColumn` and `UTF8StringForColumn`.
+
+ - Note, the `objectForColumn` (and the associted subscript operator) now returns `nil` if an invalid column name/index is passed to it. It used to return `NSNull`.
+
+ - To avoid confusion with `FMDatabaseQueue` method `inTransaction`, which performs transactions, the `FMDatabase` method to determine whether you are in a transaction or not, `inTransaction`, has been replaced with a read-only property, `isInTransaction`.
+
+ - Several functions have been converted to properties, namely, `databasePath`, `maxBusyRetryTimeInterval`, `shouldCacheStatements`, `sqliteHandle`, `hasOpenResultSets`, `lastInsertRowId`, `changes`, `goodConnection`, `columnCount`, `resultDictionary`, `applicationID`, `applicationIDString`, `userVersion`, `countOfCheckedInDatabases`, `countOfCheckedOutDatabases`, and `countOfOpenDatabases`. For Objective-C users, this has little material impact, but for Swift users, it results in a slightly more natural interface. Note: For Objective-C developers, previously versions of FMDB exposed many ivars (but we hope you weren't using them directly, anyway!), but the implmentation details for these are no longer exposed.
+
+### URL Methods
+
+In keeping with Apple's shift from paths to URLs, there are now `NSURL` renditions of the various `init` methods, previously only accepting paths.
+
## Usage
There are three main classes in FMDB:
@@ -110,8 +206,8 @@ if ([s next]) {
- `dateForColumn:`
- `dataForColumn:`
- `dataNoCopyForColumn:`
-- `UTF8StringForColumnName:`
-- `objectForColumnName:`
+- `UTF8StringForColumn:`
+- `objectForColumn:`
Each of these methods also has a `{type}ForColumnIndex:` variant that is used to retrieve the data based on the position of the column in the results, as opposed to the column's name.
@@ -188,7 +284,7 @@ In Swift, you would use `executeUpdate(values:)`, which not only is a concise Sw
do {
let identifier = 42
let name = "Liam O'Flaherty (\"the famous Irish author\")"
- let date = NSDate()
+ let date = Date()
let comment: String? = nil
try db.executeUpdate("INSERT INTO authors (identifier, name, date, comment) VALUES (?, ?, ?, ?)", values: [identifier, name, date, comment ?? NSNull()])
@@ -269,18 +365,18 @@ The Swift 3 equivalent would be:
```swift
queue.inTransaction { db, rollback in
do {
- try db?.executeUpdate("INSERT INTO myTable VALUES (?)", values: [1])
- try db?.executeUpdate("INSERT INTO myTable VALUES (?)", values: [2])
- try db?.executeUpdate("INSERT INTO myTable VALUES (?)", values: [3])
+ try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [1])
+ try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [2])
+ try db.executeUpdate("INSERT INTO myTable VALUES (?)", values: [3])
if whoopsSomethingWrongHappened {
- rollback?.pointee = true
+ rollback.pointee = true
return
}
// etc ...
} catch {
- rollback?.pointee = true
+ rollback.pointee = true
print(error)
}
}
@@ -326,10 +422,7 @@ let fileURL = try! FileManager.default
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("test.sqlite")
-guard let database = FMDatabase(path: fileURL.path) else {
- print("unable to create database")
- return
-}
+let database = FMDatabase(url: fileURL)
guard database.open() else {
print("Unable to open database")
@@ -361,7 +454,7 @@ let fileURL = try! NSFileManager.defaultManager()
.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
.URLByAppendingPathComponent("test.sqlite")
-let database = FMDatabase(path: fileURL.path)
+let database = FMDatabase(url: fileURL)
if !database.open() {
print("Unable to open database")
diff --git a/Tests/FMDatabaseAdditionsTests.m b/Tests/FMDatabaseAdditionsTests.m
index 9f6e84f..d5b37bd 100644
--- a/Tests/FMDatabaseAdditionsTests.m
+++ b/Tests/FMDatabaseAdditionsTests.m
@@ -77,8 +77,19 @@
XCTAssertEqualWithAccuracy([foo timeIntervalSinceDate:date], 0.0, 1.0, @"Dates should be the same to within a second");
}
-- (void)testTableExists
-{
+- (void)testValidate {
+ NSError *error;
+ XCTAssert([self.db validateSQL:@"create table datetest (a double, b double, c double)" error:&error]);
+ XCTAssertNil(error, @"There should be no error object");
+}
+
+- (void)testFailValidate {
+ NSError *error;
+ XCTAssertFalse([self.db validateSQL:@"blah blah blah" error:&error]);
+ XCTAssert(error, @"There should be no error object");
+}
+
+- (void)testTableExists {
XCTAssertTrue([self.db executeUpdate:@"create table t4 (a text, b text)"]);
XCTAssertTrue([self.db tableExists:@"t4"]);
@@ -91,8 +102,7 @@
}
-- (void)testColumnExists
-{
+- (void)testColumnExists {
[self.db executeUpdate:@"create table nulltest (a text, b text)"];
XCTAssertTrue([self.db columnExists:@"a" inTableWithName:@"nulltest"]);
@@ -101,7 +111,6 @@
}
- (void)testUserVersion {
-
[[self db] setUserVersion:12];
XCTAssertTrue([[self db] userVersion] == 12);
diff --git a/Tests/FMDatabasePoolTests.m b/Tests/FMDatabasePoolTests.m
index 03fb086..1173b46 100644
--- a/Tests/FMDatabasePoolTests.m
+++ b/Tests/FMDatabasePoolTests.m
@@ -8,6 +8,12 @@
#import <XCTest/XCTest.h>
+#if FMDB_SQLITE_STANDALONE
+#import <sqlite3/sqlite3.h>
+#else
+#import <sqlite3.h>
+#endif
+
@interface FMDatabasePoolTests : FMDBTempDBTests
@property FMDatabasePool *pool;
@@ -31,8 +37,7 @@
[db executeUpdate:@"insert into likefoo values ('not')"];
}
-- (void)setUp
-{
+- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
@@ -42,19 +47,97 @@
}
-- (void)tearDown
-{
+- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
-- (void)testPoolIsInitiallyEmpty
-{
+- (void)testURLOpenNoURL {
+ FMDatabasePool *pool = [[FMDatabasePool alloc] initWithURL:nil];
+ XCTAssert(pool, @"Database pool should be returned");
+ pool = nil;
+}
+
+- (void)testURLOpen {
+ NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
+ NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
+
+ FMDatabasePool *pool = [FMDatabasePool databasePoolWithURL:fileURL];
+ XCTAssert(pool, @"Database pool should be returned");
+ pool = nil;
+ [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil];
+}
+
+- (void)testURLOpenInit {
+ NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
+ NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
+
+ FMDatabasePool *pool = [[FMDatabasePool alloc] initWithURL:fileURL];
+ XCTAssert(pool, @"Database pool should be returned");
+ pool = nil;
+ [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil];
+}
+
+- (void)testURLOpenWithOptions {
+ NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
+ NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
+
+ FMDatabasePool *pool = [FMDatabasePool databasePoolWithURL:fileURL flags:SQLITE_OPEN_READWRITE];
+ [pool inDatabase:^(FMDatabase * _Nonnull db) {
+ XCTAssertNil(db, @"The database should not have been created");
+ }];
+}
+
+- (void)testURLOpenInitWithOptions {
+ NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
+ NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
+
+ FMDatabasePool *pool = [[FMDatabasePool alloc] initWithURL:fileURL flags:SQLITE_OPEN_READWRITE];
+ [pool inDatabase:^(FMDatabase * _Nonnull db) {
+ XCTAssertNil(db, @"The database should not have been created");
+ }];
+
+ pool = [[FMDatabasePool alloc] initWithURL:fileURL flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE];
+ [pool inDatabase:^(FMDatabase * _Nonnull db) {
+ XCTAssert(db, @"The database should have been created");
+
+ BOOL success = [db executeUpdate:@"CREATE TABLE foo (bar INT)"];
+ XCTAssert(success, @"Create failed");
+ success = [db executeUpdate:@"INSERT INTO foo (bar) VALUES (?)", @42];
+ XCTAssert(success, @"Insert failed");
+ }];
+
+ pool = [[FMDatabasePool alloc] initWithURL:fileURL flags:SQLITE_OPEN_READONLY];
+ [pool inDatabase:^(FMDatabase * _Nonnull db) {
+ XCTAssert(db, @"Now database pool should open have been created");
+ BOOL success = [db executeUpdate:@"CREATE TABLE baz (qux INT)"];
+ XCTAssertFalse(success, @"But updates should fail on read only database");
+ }];
+ pool = nil;
+
+ [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil];
+}
+
+- (void)testURLOpenWithOptionsVfs {
+ sqlite3_vfs vfs = *sqlite3_vfs_find(NULL);
+ vfs.zName = "MyCustomVFS";
+ XCTAssertEqual(SQLITE_OK, sqlite3_vfs_register(&vfs, 0));
+
+ NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
+ NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
+
+ FMDatabasePool *pool = [[FMDatabasePool alloc] initWithURL:fileURL flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE vfs:@"MyCustomVFS"];
+ XCTAssert(pool, @"Database pool should not have been created");
+ pool = nil;
+
+ XCTAssertEqual(SQLITE_OK, sqlite3_vfs_unregister(&vfs));
+}
+
+- (void)testPoolIsInitiallyEmpty {
XCTAssertEqual([self.pool countOfOpenDatabases], (NSUInteger)0, @"Pool should be empty on creation");
}
-- (void)testDatabaseCreation
-{
+- (void)testDatabaseCreation {
__block FMDatabase *db1;
[self.pool inDatabase:^(FMDatabase *db) {
diff --git a/Tests/FMDatabaseQueueTests.m b/Tests/FMDatabaseQueueTests.m
index b1c572a..bf46a74 100644
--- a/Tests/FMDatabaseQueueTests.m
+++ b/Tests/FMDatabaseQueueTests.m
@@ -47,6 +47,90 @@
[super tearDown];
}
+- (void)testURLOpenNoPath {
+ FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] init];
+ XCTAssert(queue, @"Database queue should be returned");
+ queue = nil;
+}
+
+- (void)testURLOpenNoURL {
+ FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithURL:nil];
+ XCTAssert(queue, @"Database queue should be returned");
+ queue = nil;
+}
+
+- (void)testURLOpen {
+ NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
+ NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
+
+ FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithURL:fileURL];
+ XCTAssert(queue, @"Database queue should be returned");
+ queue = nil;
+ [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil];
+}
+
+- (void)testURLOpenInit {
+ NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
+ NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
+
+ FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithURL:fileURL];
+ XCTAssert(queue, @"Database queue should be returned");
+ queue = nil;
+ [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil];
+}
+
+- (void)testURLOpenWithOptions {
+ NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
+ NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
+
+ FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithURL:fileURL flags:SQLITE_OPEN_READWRITE];
+ XCTAssertNil(queue, @"Database queue should not have been created");
+}
+
+- (void)testURLOpenInitWithOptions {
+ NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
+ NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
+
+ FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithURL:fileURL flags:SQLITE_OPEN_READWRITE];
+ XCTAssertNil(queue, @"Database queue should not have been created");
+
+ queue = [[FMDatabaseQueue alloc] initWithURL:fileURL flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE];
+ XCTAssert(queue, @"Database queue should have been created");
+
+ [queue inDatabase:^(FMDatabase * _Nonnull db) {
+ BOOL success = [db executeUpdate:@"CREATE TABLE foo (bar INT)"];
+ XCTAssert(success, @"Create failed");
+ success = [db executeUpdate:@"INSERT INTO foo (bar) VALUES (?)", @42];
+ XCTAssert(success, @"Insert failed");
+ }];
+ queue = nil;
+
+ queue = [[FMDatabaseQueue alloc] initWithURL:fileURL flags:SQLITE_OPEN_READONLY];
+ XCTAssert(queue, @"Now database queue should open have been created");
+ [queue inDatabase:^(FMDatabase * _Nonnull db) {
+ BOOL success = [db executeUpdate:@"CREATE TABLE baz (qux INT)"];
+ XCTAssertFalse(success, @"But updates should fail on read only database");
+ }];
+ queue = nil;
+
+ [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil];
+}
+
+- (void)testURLOpenWithOptionsVfs {
+ sqlite3_vfs vfs = *sqlite3_vfs_find(NULL);
+ vfs.zName = "MyCustomVFS";
+ XCTAssertEqual(SQLITE_OK, sqlite3_vfs_register(&vfs, 0));
+
+ NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
+ NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
+
+ FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithURL:fileURL flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE vfs:@"MyCustomVFS"];
+ XCTAssert(queue, @"Database queue should not have been created");
+ queue = nil;
+
+ XCTAssertEqual(SQLITE_OK, sqlite3_vfs_unregister(&vfs));
+}
+
- (void)testQueueSelect
{
[self.queue inDatabase:^(FMDatabase *adb) {
diff --git a/Tests/FMDatabaseTests.m b/Tests/FMDatabaseTests.m
index 9c423dc..2194570 100644
--- a/Tests/FMDatabaseTests.m
+++ b/Tests/FMDatabaseTests.m
@@ -23,8 +23,7 @@
@implementation FMDatabaseTests
-+ (void)populateDatabase:(FMDatabase *)db
-{
++ (void)populateDatabase:(FMDatabase *)db {
[db executeUpdate:@"create table test (a text, b text, c integer, d double, e double)"];
[db beginTransaction];
@@ -61,22 +60,17 @@
[db commit];
}
-- (void)setUp
-{
+- (void)setUp{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
-
}
-- (void)tearDown
-{
+- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
-
}
-- (void)testOpenWithVFS
-{
+- (void)testOpenWithVFS {
// create custom vfs
sqlite3_vfs vfs = *sqlite3_vfs_find(NULL);
vfs.zName = "MyCustomVFS";
@@ -85,25 +79,35 @@
FMDatabase *db = [[FMDatabase alloc] initWithPath:@":memory:"];
[db openWithFlags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE vfs:@"MyCustomVFS"];
XCTAssertFalse([db hadError], @"Open with a custom VFS should have succeeded");
+ XCTAssertEqual(SQLITE_OK, sqlite3_vfs_unregister(&vfs));
}
-- (void)testFailOnOpenWithUnknownVFS
-{
+- (void)testURLOpen {
+ NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
+ NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
+
+ FMDatabase *db = [FMDatabase databaseWithURL:fileURL];
+ XCTAssert(db, @"Database should be returned");
+ XCTAssertTrue([db open], @"Open should succeed");
+ XCTAssertEqualObjects([db databaseURL], fileURL);
+ XCTAssertTrue([db close], @"close should succeed");
+ [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil];
+}
+
+- (void)testFailOnOpenWithUnknownVFS {
FMDatabase *db = [[FMDatabase alloc] initWithPath:@":memory:"];
[db openWithFlags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE vfs:@"UnknownVFS"];
XCTAssertTrue([db hadError], @"Should have failed");
}
-- (void)testFailOnUnopenedDatabase
-{
+- (void)testFailOnUnopenedDatabase {
[self.db close];
XCTAssertNil([self.db executeQuery:@"select * from table"], @"Shouldn't get results from an empty table");
XCTAssertTrue([self.db hadError], @"Should have failed");
}
-- (void)testFailOnBadStatement
-{
+- (void)testFailOnBadStatement {
XCTAssertFalse([self.db executeUpdate:@"blah blah blah"], @"Invalid statement should fail");
XCTAssertTrue([self.db hadError], @"Should have failed");
}
@@ -180,17 +184,45 @@
XCTAssertFalse([self.db hadError], @"Shouldn't have any errors");
}
-- (void)testSelectWithIndexedAndKeyedSubscript
+- (void)testInvalidColumnNames
{
FMResultSet *rs = [self.db executeQuery:@"select rowid, a, b, c from test"];
XCTAssertNotNil(rs, @"Should have a non-nil result set");
+ NSString *invalidColumnName = @"foobar";
+
while ([rs next]) {
- XCTAssertEqualObjects(rs[0], rs[@"rowid"], @"Column zero should be equal to 'rowid'");
- XCTAssertEqualObjects(rs[1], rs[@"a"], @"Column 1 should be equal to 'a'");
- XCTAssertEqualObjects(rs[2], rs[@"b"], @"Column 2 should be equal to 'b'");
- XCTAssertEqualObjects(rs[3], rs[@"c"], @"Column 3 should be equal to 'c'");
+ XCTAssertNil(rs[invalidColumnName], @"Invalid column name should return nil");
+ XCTAssertNil([rs stringForColumn:invalidColumnName], @"Invalid column name should return nil");
+ XCTAssertEqual([rs UTF8StringForColumn:invalidColumnName], (const unsigned char *)0, @"Invalid column name should return nil");
+ XCTAssertNil([rs dateForColumn:invalidColumnName], @"Invalid column name should return nil");
+ XCTAssertNil([rs dataForColumn:invalidColumnName], @"Invalid column name should return nil");
+ XCTAssertNil([rs dataNoCopyForColumn:invalidColumnName], @"Invalid column name should return nil");
+ XCTAssertNil([rs objectForColumn:invalidColumnName], @"Invalid column name should return nil");
+ }
+
+ [rs close];
+ XCTAssertFalse([self.db hasOpenResultSets], @"Shouldn't have any open result sets");
+ XCTAssertFalse([self.db hadError], @"Shouldn't have any errors");
+}
+
+- (void)testInvalidColumnIndexes
+{
+ FMResultSet *rs = [self.db executeQuery:@"select rowid, a, b, c from test"];
+
+ XCTAssertNotNil(rs, @"Should have a non-nil result set");
+
+ int invalidColumnIndex = 999;
+
+ while ([rs next]) {
+ XCTAssertNil(rs[invalidColumnIndex], @"Invalid column name should return nil");
+ XCTAssertNil([rs stringForColumnIndex:invalidColumnIndex], @"Invalid column name should return nil");
+ XCTAssertEqual([rs UTF8StringForColumnIndex:invalidColumnIndex], (const unsigned char *)0, @"Invalid column name should return nil");
+ XCTAssertNil([rs dateForColumnIndex:invalidColumnIndex], @"Invalid column name should return nil");
+ XCTAssertNil([rs dataForColumnIndex:invalidColumnIndex], @"Invalid column name should return nil");
+ XCTAssertNil([rs dataNoCopyForColumnIndex:invalidColumnIndex], @"Invalid column name should return nil");
+ XCTAssertNil([rs objectForColumnIndex:invalidColumnIndex], @"Invalid column name should return nil");
}
[rs close];
@@ -532,7 +564,7 @@
XCTAssertEqualObjects([rs stringForColumn:@"t4.a"], @"one");
XCTAssertEqualObjects([rs stringForColumn:@"b"], @"two");
- XCTAssertEqual(strcmp((const char*)[rs UTF8StringForColumnName:@"b"], "two"), 0, @"String comparison should return zero");
+ XCTAssertEqual(strcmp((const char*)[rs UTF8StringForColumn:@"b"], "two"), 0, @"String comparison should return zero");
[rs close];
@@ -551,7 +583,7 @@
XCTAssertEqualObjects([rs stringForColumn:@"t4.a"], @"one");
XCTAssertEqualObjects([rs stringForColumn:@"b"], @"two");
- XCTAssertEqual(strcmp((const char*)[rs UTF8StringForColumnName:@"b"], "two"), 0, @"String comparison should return zero");
+ XCTAssertEqual(strcmp((const char*)[rs UTF8StringForColumn:@"b"], "two"), 0, @"String comparison should return zero");
[rs close];
}
@@ -803,48 +835,300 @@
}
-- (void)testCustomFunction
-{
- [self.db executeUpdate:@"create table ftest (foo text)"];
- [self.db executeUpdate:@"insert into ftest values ('hello')"];
- [self.db executeUpdate:@"insert into ftest values ('hi')"];
- [self.db executeUpdate:@"insert into ftest values ('not h!')"];
- [self.db executeUpdate:@"insert into ftest values ('definitely not h!')"];
-
- [self.db makeFunctionNamed:@"StringStartsWithH" maximumArguments:1 withBlock:^(void *context, int aargc, void **aargv) {
- if (sqlite3_value_type(aargv[0]) == SQLITE_TEXT) {
-
- @autoreleasepool {
-
- const char *c = (const char *)sqlite3_value_text(aargv[0]);
-
- NSString *s = [NSString stringWithUTF8String:c];
-
- sqlite3_result_int(context, [s hasPrefix:@"h"]);
- }
- }
- else {
- XCTFail(@"Unknown format for StringStartsWithH (%d)", sqlite3_value_type(aargv[0]));
- sqlite3_result_null(context);
+- (void)testCustomStringFunction {
+ [self createCustomFunctions];
+
+ FMResultSet *ars = [self.db executeQuery:@"SELECT RemoveDiacritics(?)", @"José"];
+ if (![ars next]) {
+ XCTFail("Should have returned value");
+ return;
+ }
+ NSString *result = [ars stringForColumnIndex:0];
+ XCTAssertEqualObjects(result, @"Jose");
+}
+
+- (void)testFailCustomStringFunction {
+ [self createCustomFunctions];
+
+ FMResultSet *rs = [self.db executeQuery:@"SELECT RemoveDiacritics(?)", @(M_PI)];
+ XCTAssert(rs, @"Prepare should have succeeded");
+
+ NSError *error;
+ BOOL success = [rs nextWithError:&error];
+ XCTAssertFalse(success, @"'next' should have failed");
+
+ XCTAssertEqualObjects(error.localizedDescription, @"Expected text");
+
+ rs = [self.db executeQuery:@"SELECT RemoveDiacritics('jose','ortega')"];
+ XCTAssertNil(rs);
+
+ error = [self.db lastError];
+
+ XCTAssert([error.localizedDescription containsString:@"wrong number of arguments"], @"Should get wrong number of arguments error, but got '%@'", error.localizedDescription);
+}
+
+- (void)testCustomDoubleFunction {
+ [self createCustomFunctions];
+
+ FMResultSet *rs = [self.db executeQuery:@"SELECT Hypotenuse(?, ?)", @(3.0), @(4.0)];
+ if (![rs next]) {
+ XCTFail("Should have returned value");
+ return;
+ }
+ double value = [rs doubleForColumnIndex:0];
+ XCTAssertEqual(value, 5.0);
+}
+
+- (void)testCustomIntFunction {
+ [self createCustomFunctions];
+
+ FMResultSet *rs = [self.db executeQuery:@"SELECT Hypotenuse(?, ?)", @(3), @(4)];
+ if (![rs next]) {
+ XCTFail("Should have returned value");
+ return;
+ }
+ int value = [rs intForColumnIndex:0];
+ XCTAssertEqual(value, 5);
+}
+
+- (void)testFailCustomNumericFunction {
+ [self createCustomFunctions];
+
+ FMResultSet *rs = [self.db executeQuery:@"SELECT Hypotenuse(?, ?)", @"foo", @"bar"];
+ NSError *error;
+ if ([rs nextWithError:&error]) {
+ XCTFail("Should have failed");
+ return;
+ }
+ XCTAssertEqualObjects(error.localizedDescription, @"Expected numeric");
+
+ rs = [self.db executeQuery:@"SELECT Hypotenuse(?)", @(3.0)];
+ XCTAssertNil(rs, @"Should fail for wrong number of arguments");
+
+ error = [self.db lastError];
+ XCTAssert([error.localizedDescription containsString:@"wrong number of arguments"], @"Should get wrong number of arguments error, but got '%@'", error.localizedDescription);
+}
+
+- (void)testCustomDataFunction {
+ [self createCustomFunctions];
+
+ NSMutableData *data = [NSMutableData data];
+ for (NSInteger i = 0; i < 256; i++) {
+ uint8_t byte = i;
+ [data appendBytes:&byte length:1];
+ }
+
+ FMResultSet *rs = [self.db executeQuery:@"SELECT SetAlternatingByteToOne(?)", data];
+ if (![rs next]) {
+ XCTFail("Should have returned value");
+ return;
+ }
+ NSData *result = [rs dataForColumnIndex:0];
+ XCTAssert(result, @"should have result");
+ XCTAssertEqual(result.length, (unsigned long)256);
+
+ for (NSInteger i = 0; i < 256; i++) {
+ uint8_t byte;
+ [result getBytes:&byte range:NSMakeRange(i, 1)];
+ if (i % 2 == 0) {
+ XCTAssertEqual(byte, (uint8_t)1);
+ } else {
+ XCTAssertEqual(byte, (uint8_t)i);
}
- }];
+ }
+}
+
+- (void)testFailCustomDataFunction {
+ [self createCustomFunctions];
- int rowCount = 0;
- FMResultSet *ars = [self.db executeQuery:@"select * from ftest where StringStartsWithH(foo)"];
- while ([ars next]) {
- rowCount++;
+ FMResultSet *rs = [self.db executeQuery:@"SELECT SetAlternatingByteToOne(?)", @"foo"];
+ XCTAssert(rs, @"Query should succeed");
+ NSError *error;
+ BOOL success = [rs nextWithError:&error];
+ XCTAssertFalse(success, @"Performing SetAlternatingByteToOne with string should fail");
+ XCTAssertEqualObjects(error.localizedDescription, @"Expected blob");
+}
+
+- (void)testCustomFunctionNullValues {
+ [self.db makeFunctionNamed:@"FunctionThatDoesntTestTypes" arguments:1 block:^(void *context, int argc, void **argv) {
+ NSData *data = [self.db valueData:argv[0]];
+ XCTAssertNil(data);
+ NSString *string = [self.db valueString:argv[0]];
+ XCTAssertNil(string);
+ int intValue = [self.db valueInt:argv[0]];
+ XCTAssertEqual(intValue, 0);
+ long longValue = [self.db valueLong:argv[0]];
+ XCTAssertEqual(longValue, 0L);
+ double doubleValue = [self.db valueDouble:argv[0]];
+ XCTAssertEqual(doubleValue, 0.0);
+ [self.db resultInt:42 context:context];
+ }];
+
+ FMResultSet *rs = [self.db executeQuery:@"SELECT FunctionThatDoesntTestTypes(?)", [NSNull null]];
+ XCTAssert(rs, @"Creating query should succeed");
+
+ NSError *error = nil;
+ if (rs) {
+ BOOL success = [rs nextWithError:&error];
+ XCTAssert(success, @"Performing query should succeed");
}
- XCTAssertEqual(rowCount, 2);
+}
+
+- (void)testCustomFunctionIntResult {
+ [self.db makeFunctionNamed:@"IntResultFunction" arguments:0 block:^(void *context, int argc, void **argv) {
+ [self.db resultInt:42 context:context];
+ }];
+
+ FMResultSet *rs = [self.db executeQuery:@"SELECT IntResultFunction()"];
+ XCTAssert(rs, @"Creating query should succeed");
+
+ BOOL success = [rs next];
+ XCTAssert(success, @"Performing query should succeed");
+
+ XCTAssertEqual([rs intForColumnIndex:0], 42);
+}
+
+- (void)testCustomFunctionLongResult {
+ [self.db makeFunctionNamed:@"LongResultFunction" arguments:0 block:^(void *context, int argc, void **argv) {
+ [self.db resultLong:42 context:context];
+ }];
+
+ FMResultSet *rs = [self.db executeQuery:@"SELECT LongResultFunction()"];
+ XCTAssert(rs, @"Creating query should succeed");
+
+ BOOL success = [rs next];
+ XCTAssert(success, @"Performing query should succeed");
+
+ XCTAssertEqual([rs longForColumnIndex:0], (long)42);
+}
+
+- (void)testCustomFunctionDoubleResult {
+ [self.db makeFunctionNamed:@"DoubleResultFunction" arguments:0 block:^(void *context, int argc, void **argv) {
+ [self.db resultDouble:0.1 context:context];
+ }];
+
+ FMResultSet *rs = [self.db executeQuery:@"SELECT DoubleResultFunction()"];
+ XCTAssert(rs, @"Creating query should succeed");
+
+ BOOL success = [rs next];
+ XCTAssert(success, @"Performing query should succeed");
+
+ XCTAssertEqual([rs doubleForColumnIndex:0], 0.1);
+}
+
+- (void)testCustomFunctionNullResult {
+ [self.db makeFunctionNamed:@"NullResultFunction" arguments:0 block:^(void *context, int argc, void **argv) {
+ [self.db resultNullInContext:context];
+ }];
+
+ FMResultSet *rs = [self.db executeQuery:@"SELECT NullResultFunction()"];
+ XCTAssert(rs, @"Creating query should succeed");
+
+ BOOL success = [rs next];
+ XCTAssert(success, @"Performing query should succeed");
+
+ XCTAssertEqualObjects([rs objectForColumnIndex:0], [NSNull null]);
+}
+
+- (void)testCustomFunctionErrorResult {
+ [self.db makeFunctionNamed:@"ErrorResultFunction" arguments:0 block:^(void *context, int argc, void **argv) {
+ [self.db resultError:@"foo" context:context];
+ [self.db resultErrorCode:42 context:context];
+ }];
+
+ FMResultSet *rs = [self.db executeQuery:@"SELECT ErrorResultFunction()"];
+ XCTAssert(rs, @"Creating query should succeed");
+
+ NSError *error = nil;
+ BOOL success = [rs nextWithError:&error];
+ XCTAssertFalse(success, @"Performing query should fail.");
+
+ XCTAssertEqualObjects(error.localizedDescription, @"foo");
+ XCTAssertEqual(error.code, 42);
+}
+
+- (void)testCustomFunctionTooBigErrorResult {
+ [self.db makeFunctionNamed:@"TooBigErrorResultFunction" arguments:0 block:^(void *context, int argc, void **argv) {
+ [self.db resultErrorTooBigInContext:context];
+ }];
+
+ FMResultSet *rs = [self.db executeQuery:@"SELECT TooBigErrorResultFunction()"];
+ XCTAssert(rs, @"Creating query should succeed");
+
+ NSError *error = nil;
+ BOOL success = [rs nextWithError:&error];
+ XCTAssertFalse(success, @"Performing query should fail.");
+
+ XCTAssertEqualObjects(error.localizedDescription, @"string or blob too big");
+ XCTAssertEqual(error.code, SQLITE_TOOBIG);
+}
+
+- (void)testCustomFunctionNoMemoryErrorResult {
+ [self.db makeFunctionNamed:@"NoMemoryErrorResultFunction" arguments:0 block:^(void *context, int argc, void **argv) {
+ [self.db resultErrorNoMemoryInContext:context];
+ }];
+
+ FMResultSet *rs = [self.db executeQuery:@"SELECT NoMemoryErrorResultFunction()"];
+ XCTAssert(rs, @"Creating query should succeed");
+
+ NSError *error = nil;
+ BOOL success = [rs nextWithError:&error];
+ XCTAssertFalse(success, @"Performing query should fail.");
+
+ XCTAssertEqualObjects(error.localizedDescription, @"out of memory");
+ XCTAssertEqual(error.code, SQLITE_NOMEM);
+}
+
+- (void)createCustomFunctions {
+ [self.db makeFunctionNamed:@"RemoveDiacritics" arguments:1 block:^(void *context, int argc, void **argv) {
+ SqliteValueType type = [self.db valueType:argv[0]];
+ if (type == SqliteValueTypeNull) {
+ [self.db resultNullInContext:context];
+ return;
+ }
+ if (type != SqliteValueTypeText) {
+ [self.db resultError:@"Expected text" context:context];
+ return;
+ }
+ NSString *string = [self.db valueString:argv[0]];
+ NSString *result = [string stringByFoldingWithOptions:NSDiacriticInsensitiveSearch locale:nil];
+ [self.db resultString:result context:context];
+ }];
+
+ [self.db makeFunctionNamed:@"Hypotenuse" arguments:2 block:^(void *context, int argc, void **argv) {
+ SqliteValueType type1 = [self.db valueType:argv[0]];
+ SqliteValueType type2 = [self.db valueType:argv[1]];
+ if (type1 != SqliteValueTypeFloat && type1 != SqliteValueTypeInteger && type2 != SqliteValueTypeFloat && type2 != SqliteValueTypeInteger) {
+ [self.db resultError:@"Expected numeric" context:context];
+ return;
+ }
+ double value1 = [self.db valueDouble:argv[0]];
+ double value2 = [self.db valueDouble:argv[1]];
+ [self.db resultDouble:hypot(value1, value2) context:context];
+ }];
+
+ [self.db makeFunctionNamed:@"SetAlternatingByteToOne" arguments:1 block:^(void *context, int argc, void **argv) {
+ SqliteValueType type = [self.db valueType:argv[0]];
+ if (type != SqliteValueTypeBlob) {
+ [self.db resultError:@"Expected blob" context:context];
+ return;
+ }
+ NSMutableData *data = [[self.db valueData:argv[0]] mutableCopy];
+ uint8_t byte = 1;
+ for (NSUInteger i = 0; i < data.length; i += 2) {
+ [data replaceBytesInRange:NSMakeRange(i, 1) withBytes:&byte];
+ }
+ [self.db resultData:data context:context];
+ }];
}
- (void)testVersionNumber {
- XCTAssertTrue([FMDatabase FMDBVersion] == 0x0262); // this is going to break everytime we bump it.
+ XCTAssertTrue([FMDatabase FMDBVersion] == 0x0270); // this is going to break everytime we bump it.
}
-- (void)testExecuteStatements
-{
+- (void)testExecuteStatements {
BOOL success;
NSString *sql = @"create table bulktest1 (id integer primary key autoincrement, x text);"
@@ -892,13 +1176,13 @@
XCTAssertTrue([rs next], @"Did not return row");
XCTAssertEqual([rs boolForColumn:@"a"], true);
- XCTAssertEqualObjects([rs objectForColumnName:@"a"], @YES);
+ XCTAssertEqualObjects([rs objectForColumn:@"a"], @YES);
XCTAssertEqual([rs boolForColumn:@"b"], false);
- XCTAssertEqualObjects([rs objectForColumnName:@"b"], @NO);
+ XCTAssertEqualObjects([rs objectForColumn:@"b"], @NO);
XCTAssertEqual([rs intForColumn:@"c"], 'x');
- XCTAssertEqualObjects([rs objectForColumnName:@"c"], @('x'));
+ XCTAssertEqualObjects([rs objectForColumn:@"c"], @('x'));
[rs close];
diff --git a/fmdb.xcodeproj/project.pbxproj b/fmdb.xcodeproj/project.pbxproj
index 37bd7d3..21cddd6 100644
--- a/fmdb.xcodeproj/project.pbxproj
+++ b/fmdb.xcodeproj/project.pbxproj
@@ -125,8 +125,6 @@
67CB1E2F19AD27D000A3CA7F /* FMDatabaseFTS3Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseFTS3Tests.m; sourceTree = "<group>"; };
8314AF3218CD73D600EC0E25 /* FMDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FMDB.h; path = src/fmdb/FMDB.h; sourceTree = "<group>"; };
831DE6FD175B7C9C001F7317 /* README.markdown */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.markdown; sourceTree = "<group>"; };
- 832F502419EC4C6B0087DCBF /* FMDatabaseVariadic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FMDatabaseVariadic.swift; path = "src/extra/Swift extensions/FMDatabaseVariadic.swift"; sourceTree = "<group>"; };
- 8352D5AC1A73DCEA003A8E09 /* FMDatabaseAdditionsVariadic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FMDatabaseAdditionsVariadic.swift; path = "src/extra/Swift extensions/FMDatabaseAdditionsVariadic.swift"; sourceTree = "<group>"; };
83C73EFE1C326AB000FFC730 /* FMDB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FMDB.framework; sourceTree = BUILT_PRODUCTS_DIR; };
83C73F0B1C326ADA00FFC730 /* FMDB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FMDB.framework; sourceTree = BUILT_PRODUCTS_DIR; };
83C73F291C326CE800FFC730 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
@@ -314,15 +312,6 @@
name = sample;
sourceTree = "<group>";
};
- 832F502319EC4C4A0087DCBF /* Swift extensions */ = {
- isa = PBXGroup;
- children = (
- 832F502419EC4C6B0087DCBF /* FMDatabaseVariadic.swift */,
- 8352D5AC1A73DCEA003A8E09 /* FMDatabaseAdditionsVariadic.swift */,
- );
- name = "Swift extensions";
- sourceTree = "<group>";
- };
832F502519EC4CA20087DCBF /* InMemoryOnDiskIO */ = {
isa = PBXGroup;
children = (
@@ -347,12 +336,12 @@
BF5D041A18416BB2008C5AA9 /* Tests */ = {
isa = PBXGroup;
children = (
+ BF5D042018416BB2008C5AA9 /* FMDatabaseTests.m */,
BF940F5D18417DEA0001E077 /* FMDatabaseAdditionsTests.m */,
+ BFE55E141841D38800CB3A63 /* FMDatabaseQueueTests.m */,
+ BFE55E121841C9A000CB3A63 /* FMDatabasePoolTests.m */,
67CB1E2F19AD27D000A3CA7F /* FMDatabaseFTS3Tests.m */,
D4A740A11A7046330058EBEE /* FMDatabaseFTS3WithModuleNameTests.m */,
- BFE55E121841C9A000CB3A63 /* FMDatabasePoolTests.m */,
- BFE55E141841D38800CB3A63 /* FMDatabaseQueueTests.m */,
- BF5D042018416BB2008C5AA9 /* FMDatabaseTests.m */,
BF940F5A18417D490001E077 /* FMDBTempDBTests.h */,
BF940F5B18417D490001E077 /* FMDBTempDBTests.m */,
3354379B19E71096005661F3 /* FMResultSetTests.m */,
@@ -386,7 +375,6 @@
CC7CE42518F5C02E00938264 /* optional extras */ = {
isa = PBXGroup;
children = (
- 832F502319EC4C4A0087DCBF /* Swift extensions */,
832F502519EC4CA20087DCBF /* InMemoryOnDiskIO */,
6767F81519AD13C300887DBC /* fts3 */,
);
@@ -564,7 +552,7 @@
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0710;
+ LastUpgradeCheck = 0820;
TargetAttributes = {
83C73EFD1C326AB000FFC730 = {
CreatedOnToolsVersion = 7.2;
@@ -745,6 +733,7 @@
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = src/sample/fmdb_Prefix.pch;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_SHADOW = YES;
INSTALL_PATH = "$(HOME)/bin";
LIBRARY_SEARCH_PATHS = "$(LIBRARY_SEARCH_PATHS)";
@@ -764,6 +753,7 @@
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = src/sample/fmdb_Prefix.pch;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_SHADOW = YES;
INSTALL_PATH = "$(HOME)/bin";
LIBRARY_SEARCH_PATHS = "$(LIBRARY_SEARCH_PATHS)";
@@ -774,11 +764,14 @@
1DEB927908733DD40010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -803,11 +796,14 @@
1DEB927A08733DD40010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -844,8 +840,9 @@
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- IPHONEOS_DEPLOYMENT_TARGET = 7.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
PUBLIC_HEADERS_FOLDER_PATH = include/FMDB;
@@ -869,9 +866,10 @@
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- IPHONEOS_DEPLOYMENT_TARGET = 7.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
PUBLIC_HEADERS_FOLDER_PATH = include/FMDB;
@@ -891,7 +889,7 @@
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
@@ -906,6 +904,7 @@
"DEBUG=1",
"$(inherited)",
);
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = src/fmdb/Info.plist;
@@ -933,7 +932,7 @@
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
@@ -943,6 +942,7 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
@@ -989,6 +989,7 @@
"DEBUG=1",
"$(inherited)",
);
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = src/fmdb/Info.plist;
@@ -1026,6 +1027,7 @@
ENABLE_NS_ASSERTIONS = NO;
FRAMEWORK_VERSION = A;
GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
@@ -1070,6 +1072,7 @@
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_PEDANTIC = NO;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -1108,6 +1111,7 @@
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -1130,6 +1134,7 @@
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
PRODUCT_NAME = FMDB;
PUBLIC_HEADERS_FOLDER_PATH = include/FMDB;
};
@@ -1142,6 +1147,7 @@
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_TREAT_WARNINGS_AS_ERRORS = YES;
PRODUCT_NAME = FMDB;
PUBLIC_HEADERS_FOLDER_PATH = include/FMDB;
ZERO_LINK = NO;
diff --git a/fmdb.xcodeproj/xcshareddata/xcschemes/FMDB MacOS.xcscheme b/fmdb.xcodeproj/xcshareddata/xcschemes/FMDB MacOS.xcscheme
index e1909bc..cd6a678 100644
--- a/fmdb.xcodeproj/xcshareddata/xcschemes/FMDB MacOS.xcscheme
+++ b/fmdb.xcodeproj/xcshareddata/xcschemes/FMDB MacOS.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0720"
+ LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
diff --git a/fmdb.xcodeproj/xcshareddata/xcschemes/FMDB iOS.xcscheme b/fmdb.xcodeproj/xcshareddata/xcschemes/FMDB iOS.xcscheme
index 9d1a1db..b70a01e 100644
--- a/fmdb.xcodeproj/xcshareddata/xcschemes/FMDB iOS.xcscheme
+++ b/fmdb.xcodeproj/xcshareddata/xcschemes/FMDB iOS.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0720"
+ LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
diff --git a/src/extra/Swift extensions/FMDatabaseAdditionsVariadic.swift b/src/extra/Swift extensions/FMDatabaseAdditionsVariadic.swift
deleted file mode 100644
index 82bc1c2..0000000
--- a/src/extra/Swift extensions/FMDatabaseAdditionsVariadic.swift
+++ /dev/null
@@ -1,117 +0,0 @@
-//
-// FMDatabaseAdditionsVariadic.swift
-// FMDB
-//
-
-import Foundation
-
-extension FMDatabase {
-
- /// Private generic function used for the variadic renditions of the FMDatabaseAdditions methods
- ///
- /// - parameter sql: The SQL statement to be used.
- /// - parameter values: The NSArray of the arguments to be bound to the ? placeholders in the SQL.
- /// - parameter completionHandler: The closure to be used to call the appropriate FMDatabase method to return the desired value.
- ///
- /// - returns: This returns the T value if value is found. Returns nil if column is NULL or upon error.
-
- private func valueForQuery<T>(sql: String, values: [AnyObject]?, completionHandler:(FMResultSet)->(T!)) -> T! {
- var result: T!
-
- if let rs = executeQuery(sql, withArgumentsInArray: values) {
- if rs.next() {
- let obj: AnyObject! = rs.objectForColumnIndex(0)
- if !(obj is NSNull) {
- result = completionHandler(rs)
- }
- }
- rs.close()
- }
-
- return result
- }
-
- /// This is a rendition of stringForQuery that handles Swift variadic parameters
- /// for the values to be bound to the ? placeholders in the SQL.
- ///
- /// - parameter sql: The SQL statement to be used.
- /// - parameter values: The values to be bound to the ? placeholders
- ///
- /// - returns: This returns string value if value is found. Returns nil if column is NULL or upon error.
-
- func stringForQuery(sql: String, _ values: AnyObject...) -> String! {
- return valueForQuery(sql, values: values) { $0.stringForColumnIndex(0) }
- }
-
- /// This is a rendition of intForQuery that handles Swift variadic parameters
- /// for the values to be bound to the ? placeholders in the SQL.
- ///
- /// - parameter sql: The SQL statement to be used.
- /// - parameter values: The values to be bound to the ? placeholders
- ///
- /// - returns: This returns integer value if value is found. Returns nil if column is NULL or upon error.
-
- func intForQuery(sql: String, _ values: AnyObject...) -> Int32! {
- return valueForQuery(sql, values: values) { $0.intForColumnIndex(0) }
- }
-
- /// This is a rendition of longForQuery that handles Swift variadic parameters
- /// for the values to be bound to the ? placeholders in the SQL.
- ///
- /// - parameter sql: The SQL statement to be used.
- /// - parameter values: The values to be bound to the ? placeholders
- ///
- /// - returns: This returns long value if value is found. Returns nil if column is NULL or upon error.
-
- func longForQuery(sql: String, _ values: AnyObject...) -> Int! {
- return valueForQuery(sql, values: values) { $0.longForColumnIndex(0) }
- }
-
- /// This is a rendition of boolForQuery that handles Swift variadic parameters
- /// for the values to be bound to the ? placeholders in the SQL.
- ///
- /// - parameter sql: The SQL statement to be used.
- /// - parameter values: The values to be bound to the ? placeholders
- ///
- /// - returns: This returns Bool value if value is found. Returns nil if column is NULL or upon error.
-
- func boolForQuery(sql: String, _ values: AnyObject...) -> Bool! {
- return valueForQuery(sql, values: values) { $0.boolForColumnIndex(0) }
- }
-
- /// This is a rendition of doubleForQuery that handles Swift variadic parameters
- /// for the values to be bound to the ? placeholders in the SQL.
- ///
- /// - parameter sql: The SQL statement to be used.
- /// - parameter values: The values to be bound to the ? placeholders
- ///
- /// - returns: This returns Double value if value is found. Returns nil if column is NULL or upon error.
-
- func doubleForQuery(sql: String, _ values: AnyObject...) -> Double! {
- return valueForQuery(sql, values: values) { $0.doubleForColumnIndex(0) }
- }
-
- /// This is a rendition of dateForQuery that handles Swift variadic parameters
- /// for the values to be bound to the ? placeholders in the SQL.
- ///
- /// - parameter sql: The SQL statement to be used.
- /// - parameter values: The values to be bound to the ? placeholders
- ///
- /// - returns: This returns NSDate value if value is found. Returns nil if column is NULL or upon error.
-
- func dateForQuery(sql: String, _ values: AnyObject...) -> NSDate! {
- return valueForQuery(sql, values: values) { $0.dateForColumnIndex(0) }
- }
-
- /// This is a rendition of dataForQuery that handles Swift variadic parameters
- /// for the values to be bound to the ? placeholders in the SQL.
- ///
- /// - parameter sql: The SQL statement to be used.
- /// - parameter values: The values to be bound to the ? placeholders
- ///
- /// - returns: This returns NSData value if value is found. Returns nil if column is NULL or upon error.
-
- func dataForQuery(sql: String, _ values: AnyObject...) -> NSData! {
- return valueForQuery(sql, values: values) { $0.dataForColumnIndex(0) }
- }
-} \ No newline at end of file
diff --git a/src/extra/Swift extensions/FMDatabaseVariadic.swift b/src/extra/Swift extensions/FMDatabaseVariadic.swift
deleted file mode 100644
index edcf055..0000000
--- a/src/extra/Swift extensions/FMDatabaseVariadic.swift
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// FMDatabaseVariadic.swift
-// FMDB
-//
-
-
-// This extension inspired by http://stackoverflow.com/a/24187932/1271826
-
-import Foundation
-
-extension FMDatabase {
-
- /// This is a rendition of executeQuery that handles Swift variadic parameters
- /// for the values to be bound to the ? placeholders in the SQL.
- ///
- /// This throws any error that occurs.
- ///
- /// - parameter sql: The SQL statement to be used.
- /// - parameter values: The values to be bound to the ? placeholders
- ///
- /// - returns: This returns FMResultSet if successful. If unsuccessful, it throws an error.
-
- func executeQuery(sql:String, _ values: AnyObject...) throws -> FMResultSet {
- return try executeQuery(sql, values: values as [AnyObject]);
- }
-
- /// This is a rendition of executeUpdate that handles Swift variadic parameters
- /// for the values to be bound to the ? placeholders in the SQL.
- ///
- /// This throws any error that occurs.
- ///
- /// - parameter sql: The SQL statement to be used.
- /// - parameter values: The values to be bound to the ? placeholders
-
- func executeUpdate(sql:String, _ values: AnyObject...) throws {
- try executeUpdate(sql, values: values as [AnyObject]);
- }
-}
diff --git a/src/extra/fts3/FMDatabase+FTS3.m b/src/extra/fts3/FMDatabase+FTS3.m
index 3903e09..708ae5e 100644
--- a/src/extra/fts3/FMDatabase+FTS3.m
+++ b/src/extra/fts3/FMDatabase+FTS3.m
@@ -292,7 +292,7 @@ static const sqlite3_tokenizer_module FMDBTokenizerModule =
- (FMTextOffsets *)offsetsForColumnIndex:(int)columnIdx
{
// The offsets() value is a space separated groups of 4 integers
- const char *rawOffsets = (const char *)sqlite3_column_text([_statement statement], columnIdx);
+ const char *rawOffsets = (const char *)sqlite3_column_text([self.statement statement], columnIdx);
return [[FMTextOffsets alloc] initWithDBOffsets:rawOffsets];
}
diff --git a/src/extra/fts3/FMTokenizers.h b/src/extra/fts3/FMTokenizers.h
index 59bce9c..4ba370b 100644
--- a/src/extra/fts3/FMTokenizers.h
+++ b/src/extra/fts3/FMTokenizers.h
@@ -9,6 +9,8 @@
#import <Foundation/Foundation.h>
#import "FMDatabase+FTS3.h"
+NS_ASSUME_NONNULL_BEGIN
+
/**
This is the base tokenizer implementation, using a CFStringTokenizer to find words.
*/
@@ -18,7 +20,7 @@
Create the tokenizer with a given locale. The locale will be used to initialize the string tokenizer and to lowercase the parsed word.
The locale can be `NULL`, in which case the current locale will be used.
*/
-- (instancetype)initWithLocale:(CFLocaleRef)locale;
+- (instancetype)initWithLocale:(CFLocaleRef _Nullable)locale;
@end
@@ -34,7 +36,7 @@
/**
Load a stop-word tokenizer using a file containing words delimited by newlines. The file should be encoded in UTF-8.
*/
-+ (instancetype)tokenizerWithFileURL:(NSURL *)wordFileURL baseTokenizer:(id<FMTokenizerDelegate>)tokenizer error:(NSError **)error;
++ (instancetype)tokenizerWithFileURL:(NSURL *)wordFileURL baseTokenizer:(id<FMTokenizerDelegate>)tokenizer error:(NSError * _Nullable *)error;
/**
Initialize an instance of the tokenizer using the set of words. The words should be lowercase if you're using the
@@ -43,3 +45,5 @@
- (instancetype)initWithWords:(NSSet *)words baseTokenizer:(id<FMTokenizerDelegate>)tokenizer;
@end
+
+NS_ASSUME_NONNULL_END
diff --git a/src/fmdb/FMDatabase.h b/src/fmdb/FMDatabase.h
index e82b6de..e527160 100644
--- a/src/fmdb/FMDatabase.h
+++ b/src/fmdb/FMDatabase.h
@@ -2,6 +2,7 @@
#import "FMResultSet.h"
#import "FMDatabasePool.h"
+NS_ASSUME_NONNULL_BEGIN
#if ! __has_feature(objc_arc)
#define FMDBAutorelease(__v) ([__v autorelease]);
@@ -70,26 +71,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
-@interface FMDatabase : NSObject {
-
- void* _db;
- NSString* _databasePath;
- BOOL _logsErrors;
- BOOL _crashOnErrors;
- BOOL _traceExecution;
- BOOL _checkedOut;
- BOOL _shouldCacheStatements;
- BOOL _isExecutingStatement;
- BOOL _inTransaction;
- NSTimeInterval _maxBusyRetryTimeInterval;
- NSTimeInterval _startBusyRetryTime;
-
- NSMutableDictionary *_cachedStatements;
- NSMutableSet *_openResultSets;
- NSMutableSet *_openFunctions;
-
- NSDateFormatter *_dateFormat;
-}
+@interface FMDatabase : NSObject
///-----------------
/// @name Properties
@@ -113,7 +95,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
/** Dictionary of cached statements */
-@property (atomic, retain) NSMutableDictionary *cachedStatements;
+@property (atomic, retain, nullable) NSMutableDictionary *cachedStatements;
///---------------------
/// @name Initialization
@@ -145,7 +127,34 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
*/
-+ (instancetype)databaseWithPath:(NSString*)inPath;
++ (instancetype)databaseWithPath:(NSString * _Nullable)inPath;
+
+/** Create a `FMDatabase` object.
+
+ An `FMDatabase` is created with a path to a SQLite database file. This path can be one of these three:
+
+ 1. A file system URL. The file does not have to exist on disk. If it does not exist, it is created for you.
+ 2. `nil`. An in-memory database is created. This database will be destroyed with the `FMDatabase` connection is closed.
+
+ For example, to create/open a database in your Mac OS X `tmp` folder:
+
+ FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
+
+ Or, in iOS, you might open a database in the app's `Documents` directory:
+
+ NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
+ NSString *dbPath = [docsPath stringByAppendingPathComponent:@"test.db"];
+ FMDatabase *db = [FMDatabase databaseWithPath:dbPath];
+
+ (For more information on temporary and in-memory databases, read the sqlite documentation on the subject: [http://www.sqlite.org/inmemorydb.html](http://www.sqlite.org/inmemorydb.html))
+
+ @param url The local file URL (not remote URL) of database file
+
+ @return `FMDatabase` object if successful; `nil` if failure.
+
+ */
+
++ (instancetype)databaseWithURL:(NSURL * _Nullable)url;
/** Initialize a `FMDatabase` object.
@@ -167,14 +176,40 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
(For more information on temporary and in-memory databases, read the sqlite documentation on the subject: [http://www.sqlite.org/inmemorydb.html](http://www.sqlite.org/inmemorydb.html))
- @param inPath Path of database file
+ @param path Path of database file.
@return `FMDatabase` object if successful; `nil` if failure.
*/
-- (instancetype)initWithPath:(NSString*)inPath;
+- (instancetype)initWithPath:(NSString * _Nullable)path;
+
+/** Initialize a `FMDatabase` object.
+
+ An `FMDatabase` is created with a local file URL to a SQLite database file. This path can be one of these three:
+
+ 1. A file system URL. The file does not have to exist on disk. If it does not exist, it is created for you.
+ 2. `nil`. An in-memory database is created. This database will be destroyed with the `FMDatabase` connection is closed.
+
+ For example, to create/open a database in your Mac OS X `tmp` folder:
+
+ FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
+
+ Or, in iOS, you might open a database in the app's `Documents` directory:
+
+ NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
+ NSString *dbPath = [docsPath stringByAppendingPathComponent:@"test.db"];
+ FMDatabase *db = [FMDatabase databaseWithPath:dbPath];
+
+ (For more information on temporary and in-memory databases, read the sqlite documentation on the subject: [http://www.sqlite.org/inmemorydb.html](http://www.sqlite.org/inmemorydb.html))
+
+ @param url The file `NSURL` of database file.
+
+ @return `FMDatabase` object if successful; `nil` if failure.
+
+ */
+- (instancetype)initWithURL:(NSURL * _Nullable)url;
///-----------------------------------
/// @name Opening and closing database
@@ -243,7 +278,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@see close
*/
-- (BOOL)openWithFlags:(int)flags vfs:(NSString *)vfsName;
+- (BOOL)openWithFlags:(int)flags vfs:(NSString * _Nullable)vfsName;
/** Closing a database connection
@@ -266,7 +301,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@return `YES` if everything succeeds, `NO` on failure.
*/
-- (BOOL)goodConnection;
+@property (nonatomic, readonly) BOOL goodConnection;
///----------------------
@@ -293,7 +328,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@see [`sqlite3_bind`](http://sqlite.org/c3ref/bind_blob.html)
*/
-- (BOOL)executeUpdate:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ...;
+- (BOOL)executeUpdate:(NSString*)sql withErrorAndBindings:(NSError * _Nullable *)outErr, ...;
/** Execute single update statement
@@ -302,7 +337,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@warning **Deprecated**: Please use `<executeUpdate:withErrorAndBindings>` instead.
*/
-- (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ... __attribute__ ((deprecated));
+- (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError * _Nullable*)outErr, ... __deprecated_msg("Use executeUpdate:withErrorAndBindings: instead");;
/** Execute single update statement
@@ -402,7 +437,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
*/
-- (BOOL)executeUpdate:(NSString*)sql values:(NSArray *)values error:(NSError * __autoreleasing *)error;
+- (BOOL)executeUpdate:(NSString*)sql values:(NSArray * _Nullable)values error:(NSError * _Nullable __autoreleasing *)error;
/** Execute single update statement
@@ -476,7 +511,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
*/
-- (BOOL)executeStatements:(NSString *)sql withResultBlock:(FMDBExecuteStatementsCallbackBlock)block;
+- (BOOL)executeStatements:(NSString *)sql withResultBlock:(FMDBExecuteStatementsCallbackBlock _Nullable)block;
/** Last insert rowid
@@ -490,7 +525,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
*/
-- (int64_t)lastInsertRowId;
+@property (nonatomic, readonly) int64_t lastInsertRowId;
/** The number of rows changed by prior SQL statement.
@@ -502,7 +537,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
*/
-- (int)changes;
+@property (nonatomic, readonly) int changes;
///-------------------------
@@ -530,7 +565,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@note You cannot use this method from Swift due to incompatibilities between Swift and Objective-C variadic implementations. Consider using `<executeQuery:values:>` instead.
*/
-- (FMResultSet *)executeQuery:(NSString*)sql, ...;
+- (FMResultSet * _Nullable)executeQuery:(NSString*)sql, ...;
/** Execute select statement
@@ -560,7 +595,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
*/
-- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
+- (FMResultSet * _Nullable)executeQueryWithFormat:(NSString*)format, ... NS_FORMAT_FUNCTION(1,2);
/** Execute select statement
@@ -579,7 +614,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@see [`FMResultSet next`](<[FMResultSet next]>)
*/
-- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments;
+- (FMResultSet * _Nullable)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments;
/** Execute select statement
@@ -608,7 +643,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
*/
-- (FMResultSet *)executeQuery:(NSString *)sql values:(NSArray *)values error:(NSError * __autoreleasing *)error;
+- (FMResultSet * _Nullable)executeQuery:(NSString *)sql values:(NSArray * _Nullable)values error:(NSError * _Nullable __autoreleasing *)error;
/** Execute select statement
@@ -626,11 +661,11 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@see [`FMResultSet next`](<[FMResultSet next]>)
*/
-- (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments;
+- (FMResultSet * _Nullable)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary * _Nullable)arguments;
// Documentation forthcoming.
-- (FMResultSet *)executeQuery:(NSString*)sql withVAList: (va_list)args;
+- (FMResultSet * _Nullable)executeQuery:(NSString *)sql withVAList:(va_list)args;
///-------------------
/// @name Transactions
@@ -643,7 +678,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@see commit
@see rollback
@see beginDeferredTransaction
- @see inTransaction
+ @see isInTransaction
*/
- (BOOL)beginTransaction;
@@ -655,7 +690,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@see commit
@see rollback
@see beginTransaction
- @see inTransaction
+ @see isInTransaction
*/
- (BOOL)beginDeferredTransaction;
@@ -669,7 +704,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@see beginTransaction
@see beginDeferredTransaction
@see rollback
- @see inTransaction
+ @see isInTransaction
*/
- (BOOL)commit;
@@ -683,22 +718,22 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@see beginTransaction
@see beginDeferredTransaction
@see commit
- @see inTransaction
+ @see isInTransaction
*/
- (BOOL)rollback;
/** Identify whether currently in a transaction or not
-
- @return `YES` if currently within transaction; `NO` if not.
-
+
@see beginTransaction
@see beginDeferredTransaction
@see commit
@see rollback
*/
-- (BOOL)inTransaction;
+@property (nonatomic, readonly) BOOL isInTransaction;
+
+- (BOOL)inTransaction __deprecated_msg("Use isInTransaction property instead");
///----------------------------------------
@@ -718,21 +753,12 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@return `YES` if there are open result sets; `NO` if not.
*/
-- (BOOL)hasOpenResultSets;
-
-/** Return whether should cache statements or not
-
- @return `YES` if should cache statements; `NO` if not.
- */
-
-- (BOOL)shouldCacheStatements;
+@property (nonatomic, readonly) BOOL hasOpenResultSets;
-/** Set whether should cache statements or not
-
- @param value `YES` if should cache statements; `NO` if not.
- */
+/** Whether should cache statements or not
+ */
-- (void)setShouldCacheStatements:(BOOL)value;
+@property (nonatomic) BOOL shouldCacheStatements;
/** Interupt pending database operation
@@ -806,12 +832,14 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
///------------------------------
/** The path of the database file
-
- @return path of database.
-
*/
-- (NSString *)databasePath;
+@property (nonatomic, readonly, nullable) NSString *databasePath;
+
+/** The file URL of the database file.
+ */
+
+@property (nonatomic, readonly, nullable) NSURL *databaseURL;
/** The underlying SQLite handle
@@ -819,7 +847,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
*/
-- (void*)sqliteHandle;
+@property (nonatomic, readonly) void *sqliteHandle;
///-----------------------------
@@ -891,12 +919,11 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
*/
-- (NSError*)lastError;
+- (NSError *)lastError;
// description forthcoming
-- (void)setMaxBusyRetryTimeInterval:(NSTimeInterval)timeoutInSeconds;
-- (NSTimeInterval)maxBusyRetryTimeInterval;
+@property (nonatomic) NSTimeInterval maxBusyRetryTimeInterval;
///------------------
@@ -915,7 +942,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@see rollbackToSavePointWithName:error:
*/
-- (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr;
+- (BOOL)startSavePointWithName:(NSString*)name error:(NSError * _Nullable *)outErr;
/** Release save point
@@ -930,7 +957,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
*/
-- (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr;
+- (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError * _Nullable *)outErr;
/** Roll back to save point
@@ -944,7 +971,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
*/
-- (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr;
+- (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError * _Nullable *)outErr;
/** Start save point
@@ -958,7 +985,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
*/
-- (NSError*)inSavePoint:(void (^)(BOOL *rollback))block;
+- (NSError * _Nullable)inSavePoint:(void (^)(BOOL *rollback))block;
///----------------------------
/// @name SQLite library status
@@ -996,48 +1023,63 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
For example:
- [queue inDatabase:^(FMDatabase *adb) {
-
- [adb executeUpdate:@"create table ftest (foo text)"];
- [adb executeUpdate:@"insert into ftest values ('hello')"];
- [adb executeUpdate:@"insert into ftest values ('hi')"];
- [adb executeUpdate:@"insert into ftest values ('not h!')"];
- [adb executeUpdate:@"insert into ftest values ('definitely not h!')"];
-
- [adb makeFunctionNamed:@"StringStartsWithH" maximumArguments:1 withBlock:^(sqlite3_context *context, int aargc, sqlite3_value **aargv) {
- if (sqlite3_value_type(aargv[0]) == SQLITE_TEXT) {
- @autoreleasepool {
- const char *c = (const char *)sqlite3_value_text(aargv[0]);
- NSString *s = [NSString stringWithUTF8String:c];
- sqlite3_result_int(context, [s hasPrefix:@"h"]);
- }
- }
- else {
- NSLog(@"Unknown formart for StringStartsWithH (%d) %s:%d", sqlite3_value_type(aargv[0]), __FUNCTION__, __LINE__);
- sqlite3_result_null(context);
- }
- }];
-
- int rowCount = 0;
- FMResultSet *ars = [adb executeQuery:@"select * from ftest where StringStartsWithH(foo)"];
- while ([ars next]) {
- rowCount++;
- NSLog(@"Does %@ start with 'h'?", [rs stringForColumnIndex:0]);
+ [db makeFunctionNamed:@"RemoveDiacritics" arguments:1 block:^(void *context, int argc, void **argv) {
+ SqliteValueType type = [self.db valueType:argv[0]];
+ if (type == SqliteValueTypeNull) {
+ [self.db resultNullInContext:context];
+ return;
}
- FMDBQuickCheck(rowCount == 2);
+ if (type != SqliteValueTypeText) {
+ [self.db resultError:@"Expected text" context:context];
+ return;
+ }
+ NSString *string = [self.db valueString:argv[0]];
+ NSString *result = [string stringByFoldingWithOptions:NSDiacriticInsensitiveSearch locale:nil];
+ [self.db resultString:result context:context];
}];
- @param name Name of function
+ FMResultSet *rs = [db executeQuery:@"SELECT * FROM employees WHERE RemoveDiacritics(first_name) LIKE 'jose'"];
+ NSAssert(rs, @"Error %@", [db lastErrorMessage]);
+
+ @param name Name of function.
- @param count Maximum number of parameters
+ @param arguments Maximum number of parameters.
- @param block The block of code for the function
+ @param block The block of code for the function.
@see [sqlite3_create_function()](http://sqlite.org/c3ref/create_function.html)
*/
-- (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(void *context, int argc, void **argv))block;
-
+- (void)makeFunctionNamed:(NSString *)name arguments:(int)arguments block:(void (^)(void *context, int argc, void * _Nonnull * _Nonnull argv))block;
+
+- (void)makeFunctionNamed:(NSString *)name maximumArguments:(int)count withBlock:(void (^)(void *context, int argc, void * _Nonnull * _Nonnull argv))block __deprecated_msg("Use makeFunctionNamed:arguments:block:");
+
+typedef NS_ENUM(int, SqliteValueType) {
+ SqliteValueTypeInteger = 1,
+ SqliteValueTypeFloat = 2,
+ SqliteValueTypeText = 3,
+ SqliteValueTypeBlob = 4,
+ SqliteValueTypeNull = 5
+};
+
+- (SqliteValueType)valueType:(void *)argv;
+
+- (int)valueInt:(void *)value;
+- (long)valueLong:(void *)value;
+- (double)valueDouble:(void *)value;
+- (NSData * _Nullable)valueData:(void *)value;
+- (NSString * _Nullable)valueString:(void *)value;
+
+- (void)resultNullInContext:(void *)context NS_SWIFT_NAME(resultNull(context:));
+- (void)resultInt:(int) value context:(void *)context;
+- (void)resultLong:(long)value context:(void *)context;
+- (void)resultDouble:(double)value context:(void *)context;
+- (void)resultData:(NSData *)data context:(void *)context;
+- (void)resultString:(NSString *)value context:(void *)context;
+- (void)resultError:(NSString *)error context:(void *)context;
+- (void)resultErrorCode:(int)errorCode context:(void *)context;
+- (void)resultErrorNoMemoryInContext:(void *)context NS_SWIFT_NAME(resultErrorNoMemory(context:));
+- (void)resultErrorTooBigInContext:(void *)context NS_SWIFT_NAME(resultErrorTooBig(context:));
///---------------------
/// @name Date formatter
@@ -1108,7 +1150,7 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
@see storeableDateFormat:
*/
-- (NSDate *)dateFromString:(NSString *)s;
+- (NSDate * _Nullable)dateFromString:(NSString *)s;
/** Convert the supplied NSDate to NSString, using the current database formatter.
@@ -1185,3 +1227,4 @@ typedef int(^FMDBExecuteStatementsCallbackBlock)(NSDictionary *resultsDictionary
#pragma clang diagnostic pop
+NS_ASSUME_NONNULL_END
diff --git a/src/fmdb/FMDatabase.m b/src/fmdb/FMDatabase.m
index 6d3b86c..b475220 100644
--- a/src/fmdb/FMDatabase.m
+++ b/src/fmdb/FMDatabase.m
@@ -8,38 +8,61 @@
#import <sqlite3.h>
#endif
-@interface FMDatabase ()
+@interface FMDatabase () {
+ void* _db;
+ BOOL _isExecutingStatement;
+ NSTimeInterval _startBusyRetryTime;
+
+ NSMutableSet *_openResultSets;
+ NSMutableSet *_openFunctions;
+
+ NSDateFormatter *_dateFormat;
+}
-- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args;
-- (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args;
+NS_ASSUME_NONNULL_BEGIN
+
+- (FMResultSet * _Nullable)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray * _Nullable)arrayArgs orDictionary:(NSDictionary * _Nullable)dictionaryArgs orVAList:(va_list)args;
+- (BOOL)executeUpdate:(NSString *)sql error:(NSError * _Nullable *)outErr withArgumentsInArray:(NSArray * _Nullable)arrayArgs orDictionary:(NSDictionary * _Nullable)dictionaryArgs orVAList:(va_list)args;
+
+NS_ASSUME_NONNULL_END
@end
@implementation FMDatabase
-@synthesize cachedStatements=_cachedStatements;
-@synthesize logsErrors=_logsErrors;
-@synthesize crashOnErrors=_crashOnErrors;
-@synthesize checkedOut=_checkedOut;
-@synthesize traceExecution=_traceExecution;
+
+// Because these two properties have all of their accessor methods implemented,
+// we have to synthesize them to get the corresponding ivars. The rest of the
+// properties have their ivars synthesized automatically for us.
+
+@synthesize shouldCacheStatements = _shouldCacheStatements;
+@synthesize maxBusyRetryTimeInterval = _maxBusyRetryTimeInterval;
#pragma mark FMDatabase instantiation and deallocation
-+ (instancetype)databaseWithPath:(NSString*)aPath {
++ (instancetype)databaseWithPath:(NSString *)aPath {
return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]);
}
++ (instancetype)databaseWithURL:(NSURL *)url {
+ return FMDBReturnAutoreleased([[self alloc] initWithURL:url]);
+}
+
- (instancetype)init {
return [self initWithPath:nil];
}
-- (instancetype)initWithPath:(NSString*)aPath {
+- (instancetype)initWithURL:(NSURL *)url {
+ return [self initWithPath:url.path];
+}
+
+- (instancetype)initWithPath:(NSString *)path {
assert(sqlite3_threadsafe()); // whoa there big boy- gotta make sure sqlite it happy with what we're going to do.
self = [super init];
if (self) {
- _databasePath = [aPath copy];
+ _databasePath = [path copy];
_openResultSets = [[NSMutableSet alloc] init];
_db = nil;
_logsErrors = YES;
@@ -70,12 +93,12 @@
#endif
}
-- (NSString *)databasePath {
- return _databasePath;
+- (NSURL *)databaseURL {
+ return _databasePath ? [NSURL fileURLWithPath:_databasePath] : nil;
}
+ (NSString*)FMDBUserVersion {
- return @"2.6.2";
+ return @"2.7.0";
}
// returns 0x0240 for version 2.4. This makes it super easy to do things like:
@@ -489,7 +512,7 @@ static int FMDBDatabaseBusyHandler(void *f, int count) {
#pragma mark Error routines
-- (NSString*)lastErrorMessage {
+- (NSString *)lastErrorMessage {
return [NSString stringWithUTF8String:sqlite3_errmsg(_db)];
}
@@ -507,7 +530,7 @@ static int FMDBDatabaseBusyHandler(void *f, int count) {
return sqlite3_extended_errcode(_db);
}
-- (NSError*)errorWithMessage:(NSString*)message {
+- (NSError*)errorWithMessage:(NSString *)message {
NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey];
return [NSError errorWithDomain:@"FMDatabase" code:sqlite3_errcode(_db) userInfo:errorMessage];
@@ -1273,7 +1296,7 @@ int FMDBExecuteBulkSQLCallback(void *theBlockAsVoid, int columns, char **values,
BOOL b = [self executeUpdate:@"rollback transaction"];
if (b) {
- _inTransaction = NO;
+ _isInTransaction = NO;
}
return b;
@@ -1283,7 +1306,7 @@ int FMDBExecuteBulkSQLCallback(void *theBlockAsVoid, int columns, char **values,
BOOL b = [self executeUpdate:@"commit transaction"];
if (b) {
- _inTransaction = NO;
+ _isInTransaction = NO;
}
return b;
@@ -1293,7 +1316,7 @@ int FMDBExecuteBulkSQLCallback(void *theBlockAsVoid, int columns, char **values,
BOOL b = [self executeUpdate:@"begin deferred transaction"];
if (b) {
- _inTransaction = YES;
+ _isInTransaction = YES;
}
return b;
@@ -1303,14 +1326,14 @@ int FMDBExecuteBulkSQLCallback(void *theBlockAsVoid, int columns, char **values,
BOOL b = [self executeUpdate:@"begin exclusive transaction"];
if (b) {
- _inTransaction = YES;
+ _isInTransaction = YES;
}
return b;
}
- (BOOL)inTransaction {
- return _inTransaction;
+ return _isInTransaction;
}
- (BOOL)interrupt
@@ -1430,12 +1453,19 @@ void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3
void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (__bridge id)sqlite3_user_data(context);
#endif
if (block) {
- block(context, argc, argv);
+ @autoreleasepool {
+ block(context, argc, argv);
+ }
}
}
+// deprecated because "arguments" parameter is not maximum argument count, but actual argument count.
-- (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(void *context, int argc, void **argv))block {
+- (void)makeFunctionNamed:(NSString *)name maximumArguments:(int)arguments withBlock:(void (^)(void *context, int argc, void **argv))block {
+ [self makeFunctionNamed:name arguments:arguments block:block];
+}
+
+- (void)makeFunctionNamed:(NSString *)name arguments:(int)arguments block:(void (^)(void *context, int argc, void **argv))block {
if (!_openFunctions) {
_openFunctions = [NSMutableSet new];
@@ -1447,21 +1477,84 @@ void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3
/* 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. */
#if ! __has_feature(objc_arc)
- sqlite3_create_function([self sqliteHandle], [name UTF8String], count, SQLITE_UTF8, (void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00);
+ sqlite3_create_function([self sqliteHandle], [name UTF8String], arguments, SQLITE_UTF8, (void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00);
#else
- sqlite3_create_function([self sqliteHandle], [name UTF8String], count, SQLITE_UTF8, (__bridge void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00);
+ sqlite3_create_function([self sqliteHandle], [name UTF8String], arguments, SQLITE_UTF8, (__bridge void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00);
#endif
}
+- (SqliteValueType)valueType:(void *)value {
+ return sqlite3_value_type(value);
+}
+
+- (int)valueInt:(void *)value {
+ return sqlite3_value_int(value);
+}
+
+- (long)valueLong:(void *)value {
+ return sqlite3_value_int64(value);
+}
+
+- (double)valueDouble:(void *)value {
+ return sqlite3_value_double(value);
+}
+
+- (NSData *)valueData:(void *)value {
+ const void *bytes = sqlite3_value_blob(value);
+ int length = sqlite3_value_bytes(value);
+ return bytes ? [NSData dataWithBytes:bytes length:length] : nil;
+}
+
+- (NSString *)valueString:(void *)value {
+ const char *cString = (const char *)sqlite3_value_text(value);
+ return cString ? [NSString stringWithUTF8String:cString] : nil;
+}
+
+- (void)resultNullInContext:(void *)context {
+ sqlite3_result_null(context);
+}
+
+- (void)resultInt:(int) value context:(void *)context {
+ sqlite3_result_int(context, value);
+}
+
+- (void)resultLong:(long)value context:(void *)context {
+ sqlite3_result_int64(context, value);
+}
+
+- (void)resultDouble:(double)value context:(void *)context {
+ sqlite3_result_double(context, value);
+}
+
+- (void)resultData:(NSData *)data context:(void *)context {
+ sqlite3_result_blob(context, data.bytes, (int)data.length, SQLITE_TRANSIENT);
+}
+
+- (void)resultString:(NSString *)value context:(void *)context {
+ sqlite3_result_text(context, [value UTF8String], -1, SQLITE_TRANSIENT);
+}
+
+- (void)resultError:(NSString *)error context:(void *)context {
+ sqlite3_result_error(context, [error UTF8String], -1);
+}
+
+- (void)resultErrorCode:(int)errorCode context:(void *)context {
+ sqlite3_result_error_code(context, errorCode);
+}
+
+- (void)resultErrorNoMemoryInContext:(void *)context {
+ sqlite3_result_error_nomem(context);
+}
+
+- (void)resultErrorTooBigInContext:(void *)context {
+ sqlite3_result_error_toobig(context);
+}
+
@end
@implementation FMStatement
-@synthesize statement=_statement;
-@synthesize query=_query;
-@synthesize useCount=_useCount;
-@synthesize inUse=_inUse;
#if ! __has_feature(objc_arc)
- (void)finalize {
@@ -1499,6 +1592,5 @@ void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3
return [NSString stringWithFormat:@"%@ %ld hit(s) for query %@", [super description], _useCount, _query];
}
-
@end
diff --git a/src/fmdb/FMDatabaseAdditions.h b/src/fmdb/FMDatabaseAdditions.h
index 9dd0b62..e2198ce 100644
--- a/src/fmdb/FMDatabaseAdditions.h
+++ b/src/fmdb/FMDatabaseAdditions.h
@@ -9,6 +9,7 @@
#import <Foundation/Foundation.h>
#import "FMDatabase.h"
+NS_ASSUME_NONNULL_BEGIN
/** Category of additions for `<FMDatabase>` class.
@@ -81,7 +82,7 @@
@note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project.
*/
-- (NSString*)stringForQuery:(NSString*)query, ...;
+- (NSString * _Nullable)stringForQuery:(NSString*)query, ...;
/** Return `NSData` value for query
@@ -93,7 +94,7 @@
@note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project.
*/
-- (NSData*)dataForQuery:(NSString*)query, ...;
+- (NSData * _Nullable)dataForQuery:(NSString*)query, ...;
/** Return `NSDate` value for query
@@ -105,7 +106,7 @@
@note To use this method from Swift, you must include `FMDatabaseAdditionsVariadic.swift` in your project.
*/
-- (NSDate*)dateForQuery:(NSString*)query, ...;
+- (NSDate * _Nullable)dateForQuery:(NSString*)query, ...;
// Notice that there's no dataNoCopyForQuery:.
@@ -141,7 +142,7 @@
@see [SQLite File Format](http://www.sqlite.org/fileformat.html)
*/
-- (FMResultSet*)getSchema;
+- (FMResultSet *)getSchema;
/** The schema of the database.
@@ -191,7 +192,7 @@
@warning Deprecated - use `<columnExists:inTableWithName:>` instead.
*/
-- (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated));
+- (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __deprecated_msg("Use columnExists:inTableWithName: instead");
/** Validate SQL statement
@@ -206,7 +207,7 @@
*/
-- (BOOL)validateSQL:(NSString*)sql error:(NSError**)error;
+- (BOOL)validateSQL:(NSString*)sql error:(NSError * _Nullable *)error;
///-----------------------------------
@@ -220,36 +221,16 @@
@see setApplicationID:
*/
-- (uint32_t)applicationID;
-
-/** Set the application ID
-
- @param appID The `uint32_t` numeric value of the application ID.
-
- @see applicationID
- */
-
-- (void)setApplicationID:(uint32_t)appID;
+@property (nonatomic) uint32_t applicationID;
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
-/** Retrieve application ID string
- @return The `NSString` value of the application ID.
+/** Retrieve application ID string
@see setApplicationIDString:
*/
-
-- (NSString*)applicationIDString;
-
-/** Set the application ID string
-
- @param string The `NSString` value of the application ID.
-
- @see applicationIDString
- */
-
-- (void)setApplicationIDString:(NSString*)string;
+@property (nonatomic, retain) NSString *applicationIDString;
#endif
@@ -259,20 +240,11 @@
/** Retrieve user version
- @return The `uint32_t` numeric value of the user version.
-
@see setUserVersion:
*/
-- (uint32_t)userVersion;
-
-/** Set the user-version
-
- @param version The `uint32_t` numeric value of the user version.
-
- @see userVersion
- */
-
-- (void)setUserVersion:(uint32_t)version;
+@property (nonatomic) uint32_t userVersion;
@end
+
+NS_ASSUME_NONNULL_END
diff --git a/src/fmdb/FMDatabaseAdditions.m b/src/fmdb/FMDatabaseAdditions.m
index 61fa747..208e69e 100644
--- a/src/fmdb/FMDatabaseAdditions.m
+++ b/src/fmdb/FMDatabaseAdditions.m
@@ -17,7 +17,7 @@
#endif
@interface FMDatabase (PrivateStuff)
-- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args;
+- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray * _Nullable)arrayArgs orDictionary:(NSDictionary * _Nullable)dictionaryArgs orVAList:(va_list)args;
@end
@implementation FMDatabase (FMDatabaseAdditions)
@@ -34,7 +34,7 @@ type ret = [resultSet sel:0]; \
return ret;
-- (NSString*)stringForQuery:(NSString*)query, ... {
+- (NSString *)stringForQuery:(NSString*)query, ... {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex);
}
@@ -222,12 +222,11 @@ return ret;
#pragma clang diagnostic pop
-
- (BOOL)validateSQL:(NSString*)sql error:(NSError**)error {
sqlite3_stmt *pStmt = NULL;
BOOL validationSucceeded = YES;
- int rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
+ int rc = sqlite3_prepare_v2([self sqliteHandle], [sql UTF8String], -1, &pStmt, 0);
if (rc != SQLITE_OK) {
validationSucceeded = NO;
if (error) {
diff --git a/src/fmdb/FMDatabasePool.h b/src/fmdb/FMDatabasePool.h
index a748a93..c32db1f 100755
--- a/src/fmdb/FMDatabasePool.h
+++ b/src/fmdb/FMDatabasePool.h
@@ -8,6 +8,8 @@
#import <Foundation/Foundation.h>
+NS_ASSUME_NONNULL_BEGIN
+
@class FMDatabase;
/** Pool of `<FMDatabase>` objects.
@@ -28,28 +30,15 @@
in the main.m file.
*/
-@interface FMDatabasePool : NSObject {
- NSString *_path;
-
- dispatch_queue_t _lockQueue;
-
- NSMutableArray *_databaseInPool;
- NSMutableArray *_databaseOutPool;
-
- __unsafe_unretained id _delegate;
-
- NSUInteger _maximumNumberOfDatabasesToCreate;
- int _openFlags;
- NSString *_vfsName;
-}
+@interface FMDatabasePool : NSObject
/** Database path */
-@property (atomic, retain) NSString *path;
+@property (atomic, copy, nullable) NSString *path;
/** Delegate object */
-@property (atomic, assign) id delegate;
+@property (atomic, assign, nullable) id delegate;
/** Maximum number of databases to create */
@@ -61,7 +50,7 @@
/** Custom virtual file system name */
-@property (atomic, copy) NSString *vfsName;
+@property (atomic, copy, nullable) NSString *vfsName;
///---------------------
@@ -69,53 +58,102 @@
///---------------------
/** Create pool using path.
-
+
@param aPath The file path of the database.
+
+ @return The `FMDatabasePool` object. `nil` on error.
+ */
++ (instancetype)databasePoolWithPath:(NSString * _Nullable)aPath;
+
+/** Create pool using file URL.
+
+ @param url The file `NSURL` of the database.
+
@return The `FMDatabasePool` object. `nil` on error.
*/
-+ (instancetype)databasePoolWithPath:(NSString*)aPath;
++ (instancetype)databasePoolWithURL:(NSURL * _Nullable)url;
/** Create pool 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 openFlags Flags passed to the openWithFlags method of the database.
+
+ @return The `FMDatabasePool` object. `nil` on error.
+ */
++ (instancetype)databasePoolWithPath:(NSString * _Nullable)aPath flags:(int)openFlags;
+
+/** Create pool using file URL and specified flags
+
+ @param url The file `NSURL` of the database.
+ @param openFlags Flags passed to the openWithFlags method of the database.
+
@return The `FMDatabasePool` object. `nil` on error.
*/
-+ (instancetype)databasePoolWithPath:(NSString*)aPath flags:(int)openFlags;
++ (instancetype)databasePoolWithURL:(NSURL * _Nullable)url flags:(int)openFlags;
/** Create pool using path.
-
+
@param aPath The file path of the database.
+
+ @return The `FMDatabasePool` object. `nil` on error.
+ */
+
+- (instancetype)initWithPath:(NSString * _Nullable)aPath;
+/** Create pool using file URL.
+
+ @param url The file `NSURL of the database.
+
@return The `FMDatabasePool` object. `nil` on error.
*/
-- (instancetype)initWithPath:(NSString*)aPath;
+- (instancetype)initWithURL:(NSURL * _Nullable)url;
/** Create pool using path and specified flags.
-
+
@param aPath The file path of the database.
@param openFlags Flags passed to the openWithFlags method of the database
+
+ @return The `FMDatabasePool` object. `nil` on error.
+ */
+
+- (instancetype)initWithPath:(NSString * _Nullable)aPath flags:(int)openFlags;
+/** Create pool using file URL and specified flags.
+
+ @param url The file `NSURL` of the database.
+ @param openFlags Flags passed to the openWithFlags method of the database
+
@return The `FMDatabasePool` object. `nil` on error.
*/
-- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags;
+- (instancetype)initWithURL:(NSURL * _Nullable)url flags:(int)openFlags;
/** Create pool 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 `FMDatabasePool` object. `nil` on error.
+ */
+
+- (instancetype)initWithPath:(NSString * _Nullable)aPath flags:(int)openFlags vfs:(NSString * _Nullable)vfsName;
+/** Create pool using file URL and specified flags.
+
+ @param url The file `NSURL` 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 `FMDatabasePool` object. `nil` on error.
*/
-- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName;
+- (instancetype)initWithURL:(NSURL * _Nullable)url flags:(int)openFlags vfs:(NSString * _Nullable)vfsName;
/** Returns the Class of 'FMDatabase' subclass, that will be used to instantiate database object.
@@ -131,25 +169,19 @@
///------------------------------------------------
/** Number of checked-in databases in pool
-
- @returns Number of databases
*/
-- (NSUInteger)countOfCheckedInDatabases;
+@property (nonatomic, readonly) NSUInteger countOfCheckedInDatabases;
/** Number of checked-out databases in pool
-
- @returns Number of databases
*/
-- (NSUInteger)countOfCheckedOutDatabases;
+@property (nonatomic, readonly) NSUInteger countOfCheckedOutDatabases;
/** Total number of databases in pool
-
- @returns Number of databases
*/
-- (NSUInteger)countOfOpenDatabases;
+@property (nonatomic, readonly) NSUInteger countOfOpenDatabases;
/** Release all databases in pool */
@@ -189,7 +221,7 @@
@warning 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 startSavePointWithName:error:]>` instead.
*/
-- (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block;
+- (NSError * _Nullable)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block;
@end
@@ -223,3 +255,4 @@
@end
+NS_ASSUME_NONNULL_END
diff --git a/src/fmdb/FMDatabasePool.m b/src/fmdb/FMDatabasePool.m
index c7fdc19..41a5985 100755
--- a/src/fmdb/FMDatabasePool.m
+++ b/src/fmdb/FMDatabasePool.m
@@ -15,7 +15,12 @@
#import "FMDatabasePool.h"
#import "FMDatabase.h"
-@interface FMDatabasePool()
+@interface FMDatabasePool () {
+ dispatch_queue_t _lockQueue;
+
+ NSMutableArray *_databaseInPool;
+ NSMutableArray *_databaseOutPool;
+}
- (void)pushDatabaseBackInPool:(FMDatabase*)db;
- (FMDatabase*)db;
@@ -30,14 +35,26 @@
@synthesize openFlags=_openFlags;
-+ (instancetype)databasePoolWithPath:(NSString*)aPath {
++ (instancetype)databasePoolWithPath:(NSString *)aPath {
return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]);
}
-+ (instancetype)databasePoolWithPath:(NSString*)aPath flags:(int)openFlags {
++ (instancetype)databasePoolWithURL:(NSURL *)url {
+ return FMDBReturnAutoreleased([[self alloc] initWithPath:url.path]);
+}
+
++ (instancetype)databasePoolWithPath:(NSString *)aPath flags:(int)openFlags {
return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath flags:openFlags]);
}
++ (instancetype)databasePoolWithURL:(NSURL *)url flags:(int)openFlags {
+ return FMDBReturnAutoreleased([[self alloc] initWithPath:url.path flags:openFlags]);
+}
+
+- (instancetype)initWithURL:(NSURL *)url flags:(int)openFlags vfs:(NSString *)vfsName {
+ return [self initWithPath:url.path flags:openFlags vfs:vfsName];
+}
+
- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName {
self = [super init];
@@ -54,16 +71,23 @@
return self;
}
-- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags {
+- (instancetype)initWithPath:(NSString *)aPath flags:(int)openFlags {
return [self initWithPath:aPath flags:openFlags vfs:nil];
}
-- (instancetype)initWithPath:(NSString*)aPath
-{
+- (instancetype)initWithURL:(NSURL *)url flags:(int)openFlags {
+ return [self initWithPath:url.path flags:openFlags vfs:nil];
+}
+
+- (instancetype)initWithPath:(NSString*)aPath {
// default flags for sqlite3_open
return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE];
}
+- (instancetype)initWithURL:(NSURL *)url {
+ return [self initWithPath:url.path];
+}
+
- (instancetype)init {
return [self initWithPath:nil];
}
@@ -78,6 +102,7 @@
FMDBRelease(_path);
FMDBRelease(_databaseInPool);
FMDBRelease(_databaseOutPool);
+ FMDBRelease(_vfsName);
if (_lockQueue) {
FMDBDispatchQueueRelease(_lockQueue);
diff --git a/src/fmdb/FMDatabaseQueue.h b/src/fmdb/FMDatabaseQueue.h
index 816031e..9c95027 100755
--- a/src/fmdb/FMDatabaseQueue.h
+++ b/src/fmdb/FMDatabaseQueue.h
@@ -8,6 +8,8 @@
#import <Foundation/Foundation.h>
+NS_ASSUME_NONNULL_BEGIN
+
@class FMDatabase;
/** To perform queries and updates on multiple threads, you'll want to use `FMDatabaseQueue`.
@@ -60,17 +62,10 @@
*/
-@interface FMDatabaseQueue : NSObject {
- NSString *_path;
- dispatch_queue_t _queue;
- FMDatabase *_db;
- int _openFlags;
- NSString *_vfsName;
-}
-
+@interface FMDatabaseQueue : NSObject
/** Path of database */
-@property (atomic, retain) NSString *path;
+@property (atomic, retain, nullable) NSString *path;
/** Open flags */
@@ -78,7 +73,7 @@
/** Custom virtual file system name */
-@property (atomic, copy) NSString *vfsName;
+@property (atomic, copy, nullable) NSString *vfsName;
///----------------------------------------------------
/// @name Initialization, opening, and closing of queue
@@ -91,35 +86,72 @@
@return The `FMDatabaseQueue` object. `nil` on error.
*/
-+ (instancetype)databaseQueueWithPath:(NSString*)aPath;
++ (instancetype)databaseQueueWithPath:(NSString * _Nullable)aPath;
+
+/** Create queue using file URL.
+
+ @param url The file `NSURL` of the database.
+
+ @return The `FMDatabaseQueue` object. `nil` on error.
+ */
+
++ (instancetype)databaseQueueWithURL:(NSURL * _Nullable)url;
/** 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 openFlags Flags passed to the openWithFlags method of the database.
@return The `FMDatabaseQueue` object. `nil` on error.
*/
-+ (instancetype)databaseQueueWithPath:(NSString*)aPath flags:(int)openFlags;
++ (instancetype)databaseQueueWithPath:(NSString * _Nullable)aPath flags:(int)openFlags;
-/** Create queue using path.
+/** Create queue using file URL and specified flags.
+
+ @param url The file `NSURL` of the database.
+ @param openFlags Flags passed to the openWithFlags method of the database.
+
+ @return The `FMDatabaseQueue` object. `nil` on error.
+ */
++ (instancetype)databaseQueueWithURL:(NSURL * _Nullable)url flags:(int)openFlags;
+/** Create queue using path.
+
@param aPath The file path of the database.
+
+ @return The `FMDatabaseQueue` object. `nil` on error.
+ */
+- (instancetype)initWithPath:(NSString * _Nullable)aPath;
+
+/** Create queue using file URL.
+
+ @param url The file `NSURL of the database.
+
@return The `FMDatabaseQueue` object. `nil` on error.
*/
-- (instancetype)initWithPath:(NSString*)aPath;
+- (instancetype)initWithURL:(NSURL * _Nullable)url;
/** 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 openFlags Flags passed to the openWithFlags method of the database.
+
+ @return The `FMDatabaseQueue` object. `nil` on error.
+ */
+
+- (instancetype)initWithPath:(NSString * _Nullable)aPath flags:(int)openFlags;
+
+/** Create queue using file URL and specified flags.
+
+ @param url The file path of the database.
+ @param openFlags Flags passed to the openWithFlags method of the database.
@return The `FMDatabaseQueue` object. `nil` on error.
*/
-- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags;
+- (instancetype)initWithURL:(NSURL * _Nullable)url flags:(int)openFlags;
/** Create queue using path and specified flags.
@@ -130,7 +162,18 @@
@return The `FMDatabaseQueue` object. `nil` on error.
*/
-- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName;
+- (instancetype)initWithPath:(NSString * _Nullable)aPath flags:(int)openFlags vfs:(NSString * _Nullable)vfsName;
+
+/** Create queue using file URL and specified flags.
+
+ @param url The file `NSURL 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)initWithURL:(NSURL * _Nullable)url flags:(int)openFlags vfs:(NSString * _Nullable)vfsName;
/** Returns the Class of 'FMDatabase' subclass, that will be used to instantiate database object.
@@ -185,7 +228,8 @@
// 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;
+- (NSError * _Nullable)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block;
@end
+NS_ASSUME_NONNULL_END
diff --git a/src/fmdb/FMDatabaseQueue.m b/src/fmdb/FMDatabaseQueue.m
index 6e45549..f3f30cf 100755
--- a/src/fmdb/FMDatabaseQueue.m
+++ b/src/fmdb/FMDatabaseQueue.m
@@ -29,15 +29,16 @@
* the queue's dispatch queue, which should not happen and causes a deadlock.
*/
static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey;
-
-@implementation FMDatabaseQueue
-@synthesize path = _path;
-@synthesize openFlags = _openFlags;
-@synthesize vfsName = _vfsName;
+@interface FMDatabaseQueue () {
+ dispatch_queue_t _queue;
+ FMDatabase *_db;
+}
+@end
-+ (instancetype)databaseQueueWithPath:(NSString*)aPath {
-
+@implementation FMDatabaseQueue
+
++ (instancetype)databaseQueueWithPath:(NSString *)aPath {
FMDatabaseQueue *q = [[self alloc] initWithPath:aPath];
FMDBAutorelease(q);
@@ -45,8 +46,11 @@ static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey
return q;
}
-+ (instancetype)databaseQueueWithPath:(NSString*)aPath flags:(int)openFlags {
-
++ (instancetype)databaseQueueWithURL:(NSURL *)url {
+ return [self databaseQueueWithPath:url.path];
+}
+
++ (instancetype)databaseQueueWithPath:(NSString *)aPath flags:(int)openFlags {
FMDatabaseQueue *q = [[self alloc] initWithPath:aPath flags:openFlags];
FMDBAutorelease(q);
@@ -54,12 +58,19 @@ static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey
return q;
}
++ (instancetype)databaseQueueWithURL:(NSURL *)url flags:(int)openFlags {
+ return [self databaseQueueWithPath:url.path flags:openFlags];
+}
+
+ (Class)databaseClass {
return [FMDatabase class];
}
+- (instancetype)initWithURL:(NSURL *)url flags:(int)openFlags vfs:(NSString *)vfsName {
+ return [self initWithPath:url.path flags:openFlags vfs:vfsName];
+}
+
- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName {
-
self = [super init];
if (self != nil) {
@@ -89,12 +100,19 @@ static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey
return self;
}
-- (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags {
+- (instancetype)initWithPath:(NSString *)aPath flags:(int)openFlags {
return [self initWithPath:aPath flags:openFlags vfs:nil];
}
-- (instancetype)initWithPath:(NSString*)aPath {
-
+- (instancetype)initWithURL:(NSURL *)url flags:(int)openFlags {
+ return [self initWithPath:url.path flags:openFlags vfs:nil];
+}
+
+- (instancetype)initWithURL:(NSURL *)url {
+ return [self initWithPath:url.path];
+}
+
+- (instancetype)initWithPath:(NSString *)aPath {
// default flags for sqlite3_open
return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE vfs:nil];
}
@@ -105,9 +123,9 @@ static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey
- (void)dealloc {
-
FMDBRelease(_db);
FMDBRelease(_path);
+ FMDBRelease(_vfsName);
if (_queue) {
FMDBDispatchQueueRelease(_queue);
@@ -128,8 +146,7 @@ static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey
FMDBRelease(self);
}
-- (void)interrupt
-{
+- (void)interrupt {
[[self database] interrupt];
}
@@ -184,7 +201,6 @@ static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey
FMDBRelease(self);
}
-
- (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block {
FMDBRetain(self);
dispatch_sync(_queue, ^() {
diff --git a/src/fmdb/FMResultSet.h b/src/fmdb/FMResultSet.h
index af0433b..8057291 100644
--- a/src/fmdb/FMResultSet.h
+++ b/src/fmdb/FMResultSet.h
@@ -1,5 +1,7 @@
#import <Foundation/Foundation.h>
+NS_ASSUME_NONNULL_BEGIN
+
#ifndef __has_feature // Optional.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
@@ -22,13 +24,9 @@
- `<FMDatabase>`
*/
-@interface FMResultSet : NSObject {
- FMDatabase *_parentDB;
- FMStatement *_statement;
-
- NSString *_query;
- NSMutableDictionary *_columnNameToIndexMap;
-}
+@interface FMResultSet : NSObject
+
+@property (nonatomic, retain, nullable) FMDatabase *parentDB;
///-----------------
/// @name Properties
@@ -36,7 +34,7 @@
/** Executed query */
-@property (atomic, retain) NSString *query;
+@property (atomic, retain, nullable) NSString *query;
/** `NSMutableDictionary` mapping column names to numeric index */
@@ -44,7 +42,7 @@
/** `FMStatement` used by result set. */
-@property (atomic, retain) FMStatement *statement;
+@property (atomic, retain, nullable) FMStatement *statement;
///------------------------------------
/// @name Creating and closing database
@@ -65,8 +63,6 @@
- (void)close;
-- (void)setParentDB:(FMDatabase *)newDb;
-
///---------------------------------------
/// @name Iterating through the result set
///---------------------------------------
@@ -93,7 +89,7 @@
@see hasAnotherRow
*/
-- (BOOL)nextWithError:(NSError **)outErr;
+- (BOOL)nextWithError:(NSError * _Nullable *)outErr;
/** Did the last call to `<next>` succeed in retrieving another row?
@@ -115,7 +111,7 @@
@return Integer value of the number of columns.
*/
-- (int)columnCount;
+@property (nonatomic, readonly) int columnCount;
/** Column index for column name
@@ -133,7 +129,7 @@
@return columnName `NSString` value of the name of the column.
*/
-- (NSString*)columnNameForIndex:(int)columnIdx;
+- (NSString * _Nullable)columnNameForIndex:(int)columnIdx;
/** Result set integer value for column.
@@ -249,39 +245,39 @@
@param columnName `NSString` value of the name of the column.
- @return `NSString` value of the result set's column.
+ @return String value of the result set's column.
*/
-- (NSString*)stringForColumn:(NSString*)columnName;
+- (NSString * _Nullable)stringForColumn:(NSString*)columnName;
/** Result set `NSString` value for column.
@param columnIdx Zero-based index for column.
- @return `NSString` value of the result set's column.
+ @return String value of the result set's column.
*/
-- (NSString*)stringForColumnIndex:(int)columnIdx;
+- (NSString * _Nullable)stringForColumnIndex:(int)columnIdx;
/** Result set `NSDate` value for column.
@param columnName `NSString` value of the name of the column.
- @return `NSDate` value of the result set's column.
+ @return Date value of the result set's column.
*/
-- (NSDate*)dateForColumn:(NSString*)columnName;
+- (NSDate * _Nullable)dateForColumn:(NSString*)columnName;
/** Result set `NSDate` value for column.
@param columnIdx Zero-based index for column.
- @return `NSDate` value of the result set's column.
+ @return Date value of the result set's column.
*/
-- (NSDate*)dateForColumnIndex:(int)columnIdx;
+- (NSDate * _Nullable)dateForColumnIndex:(int)columnIdx;
/** Result set `NSData` value for column.
@@ -289,20 +285,20 @@
@param columnName `NSString` value of the name of the column.
- @return `NSData` value of the result set's column.
+ @return Data value of the result set's column.
*/
-- (NSData*)dataForColumn:(NSString*)columnName;
+- (NSData * _Nullable)dataForColumn:(NSString*)columnName;
/** Result set `NSData` value for column.
@param columnIdx Zero-based index for column.
- @return `NSData` value of the result set's column.
+ @return Data value of the result set's column.
*/
-- (NSData*)dataForColumnIndex:(int)columnIdx;
+- (NSData * _Nullable)dataForColumnIndex:(int)columnIdx;
/** Result set `(const unsigned char *)` value for column.
@@ -311,7 +307,9 @@
@return `(const unsigned char *)` value of the result set's column.
*/
-- (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName;
+- (const unsigned char * _Nullable)UTF8StringForColumn:(NSString*)columnName;
+
+- (const unsigned char * _Nullable)UTF8StringForColumnName:(NSString*)columnName __deprecated_msg("Use UTF8StringForColumn instead");
/** Result set `(const unsigned char *)` value for column.
@@ -320,18 +318,20 @@
@return `(const unsigned char *)` value of the result set's column.
*/
-- (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx;
+- (const unsigned char * _Nullable)UTF8StringForColumnIndex:(int)columnIdx;
/** Result set object for column.
- @param columnName `NSString` value of the name of the column.
+ @param columnName Name of the column.
@return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object.
@see objectForKeyedSubscript:
*/
-- (id)objectForColumnName:(NSString*)columnName;
+- (id _Nullable)objectForColumn:(NSString*)columnName;
+
+- (id _Nullable)objectForColumnName:(NSString*)columnName __deprecated_msg("Use objectForColumn instead");
/** Result set object for column.
@@ -342,7 +342,7 @@
@see objectAtIndexedSubscript:
*/
-- (id)objectForColumnIndex:(int)columnIdx;
+- (id _Nullable)objectForColumnIndex:(int)columnIdx;
/** Result set object for column.
@@ -363,7 +363,7 @@
@return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object.
*/
-- (id)objectForKeyedSubscript:(NSString *)columnName;
+- (id _Nullable)objectForKeyedSubscript:(NSString *)columnName;
/** Result set object for column.
@@ -384,13 +384,13 @@
@return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object.
*/
-- (id)objectAtIndexedSubscript:(int)columnIdx;
+- (id _Nullable)objectAtIndexedSubscript:(int)columnIdx;
/** Result set `NSData` value for column.
@param columnName `NSString` value of the name of the column.
- @return `NSData` value of the result set's column.
+ @return Data value of the result set's column.
@warning If you are going to use this data after you iterate over the next row, or after you close the
result set, make sure to make a copy of the data first (or just use `<dataForColumn:>`/`<dataForColumnIndex:>`)
@@ -398,13 +398,13 @@ If you don't, you're going to be in a world of hurt when you try and use the dat
*/
-- (NSData*)dataNoCopyForColumn:(NSString*)columnName NS_RETURNS_NOT_RETAINED;
+- (NSData * _Nullable)dataNoCopyForColumn:(NSString *)columnName NS_RETURNS_NOT_RETAINED;
/** Result set `NSData` value for column.
@param columnIdx Zero-based index for column.
- @return `NSData` value of the result set's column.
+ @return Data value of the result set's column.
@warning If you are going to use this data after you iterate over the next row, or after you close the
result set, make sure to make a copy of the data first (or just use `<dataForColumn:>`/`<dataForColumnIndex:>`)
@@ -412,7 +412,7 @@ If you don't, you're going to be in a world of hurt when you try and use the dat
*/
-- (NSData*)dataNoCopyForColumnIndex:(int)columnIdx NS_RETURNS_NOT_RETAINED;
+- (NSData * _Nullable)dataNoCopyForColumnIndex:(int)columnIdx NS_RETURNS_NOT_RETAINED;
/** Is the column `NULL`?
@@ -435,12 +435,10 @@ If you don't, you're going to be in a world of hurt when you try and use the dat
/** Returns a dictionary of the row results mapped to case sensitive keys of the column names.
- @returns `NSDictionary` of the row results.
-
@warning The keys to the dictionary are case sensitive of the column names.
*/
-- (NSDictionary*)resultDictionary;
+@property (nonatomic, readonly, nullable) NSDictionary *resultDictionary;
/** Returns a dictionary of the row results
@@ -449,7 +447,7 @@ If you don't, you're going to be in a world of hurt when you try and use the dat
@warning **Deprecated**: Please use `<resultDictionary>` instead. Also, beware that `<resultDictionary>` is case sensitive!
*/
-- (NSDictionary*)resultDict __attribute__ ((deprecated));
+- (NSDictionary * _Nullable)resultDict __deprecated_msg("Use resultDictionary instead");
///-----------------------------
/// @name Key value coding magic
@@ -466,3 +464,4 @@ If you don't, you're going to be in a world of hurt when you try and use the dat
@end
+NS_ASSUME_NONNULL_END
diff --git a/src/fmdb/FMResultSet.m b/src/fmdb/FMResultSet.m
index 6cb926d..811e47c 100644
--- a/src/fmdb/FMResultSet.m
+++ b/src/fmdb/FMResultSet.m
@@ -12,10 +12,12 @@
- (void)resultSetDidClose:(FMResultSet *)resultSet;
@end
+@interface FMResultSet () {
+ NSMutableDictionary *_columnNameToIndexMap;
+}
+@end
@implementation FMResultSet
-@synthesize query=_query;
-@synthesize statement=_statement;
+ (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB {
@@ -100,7 +102,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
-- (NSDictionary*)resultDict {
+- (NSDictionary *)resultDict {
NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]);
@@ -228,8 +230,6 @@
return -1;
}
-
-
- (int)intForColumn:(NSString*)columnName {
return [self intForColumnIndex:[self columnIndexForName:columnName]];
}
@@ -278,9 +278,9 @@
return sqlite3_column_double([_statement statement], columnIdx);
}
-- (NSString*)stringForColumnIndex:(int)columnIdx {
+- (NSString *)stringForColumnIndex:(int)columnIdx {
- if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
+ if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0) || columnIdx >= sqlite3_column_count([_statement statement])) {
return nil;
}
@@ -304,7 +304,7 @@
- (NSDate*)dateForColumnIndex:(int)columnIdx {
- if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
+ if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0) || columnIdx >= sqlite3_column_count([_statement statement])) {
return nil;
}
@@ -318,7 +318,7 @@
- (NSData*)dataForColumnIndex:(int)columnIdx {
- if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
+ if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0) || columnIdx >= sqlite3_column_count([_statement statement])) {
return nil;
}
@@ -339,7 +339,7 @@
- (NSData*)dataNoCopyForColumnIndex:(int)columnIdx {
- if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
+ if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0) || columnIdx >= sqlite3_column_count([_statement statement])) {
return nil;
}
@@ -362,18 +362,26 @@
- (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx {
- if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
+ if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0) || columnIdx >= sqlite3_column_count([_statement statement])) {
return nil;
}
return sqlite3_column_text([_statement statement], columnIdx);
}
-- (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName {
+- (const unsigned char *)UTF8StringForColumn:(NSString*)columnName {
return [self UTF8StringForColumnIndex:[self columnIndexForName:columnName]];
}
+- (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName {
+ return [self UTF8StringForColumn:columnName];
+}
+
- (id)objectForColumnIndex:(int)columnIdx {
+ if (columnIdx < 0 || columnIdx >= sqlite3_column_count([_statement statement])) {
+ return nil;
+ }
+
int columnType = sqlite3_column_type([_statement statement], columnIdx);
id returnValue = nil;
@@ -400,6 +408,10 @@
}
- (id)objectForColumnName:(NSString*)columnName {
+ return [self objectForColumn:columnName];
+}
+
+- (id)objectForColumn:(NSString*)columnName {
return [self objectForColumnIndex:[self columnIndexForName:columnName]];
}
@@ -408,16 +420,12 @@
return [NSString stringWithUTF8String: sqlite3_column_name([_statement statement], columnIdx)];
}
-- (void)setParentDB:(FMDatabase *)newDb {
- _parentDB = newDb;
-}
-
- (id)objectAtIndexedSubscript:(int)columnIdx {
return [self objectForColumnIndex:columnIdx];
}
- (id)objectForKeyedSubscript:(NSString *)columnName {
- return [self objectForColumnName:columnName];
+ return [self objectForColumn:columnName];
}
diff --git a/src/fmdb/Info.plist b/src/fmdb/Info.plist
index 35cb69f..febd1af 100644
--- a/src/fmdb/Info.plist
+++ b/src/fmdb/Info.plist
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
- <string>2.6.2</string>
+ <string>2.7.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
diff --git a/src/sample/main.m b/src/sample/main.m
index ea9b3d3..682c89c 100644
--- a/src/sample/main.m
+++ b/src/sample/main.m
@@ -1048,13 +1048,9 @@ int main (int argc, const char * argv[]) {
[adb makeFunctionNamed:@"StringStartsWithH" maximumArguments:1 withBlock:^(/*sqlite3_context*/ void *context, int aargc, /*sqlite3_value*/ void **aargv) {
if (sqlite3_value_type(aargv[0]) == SQLITE_TEXT) {
-
@autoreleasepool {
-
const char *c = (const char *)sqlite3_value_text(aargv[0]);
-
NSString *s = [NSString stringWithUTF8String:c];
-
sqlite3_result_int(context, [s hasPrefix:@"h"]);
}
}