Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES_AND_TODO_LIST.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ Zip, nada, zilch. Got any ideas?

If you would like to contribute some code ... awesome! I just ask that you make it conform to the coding conventions already set in here, and to add the necessary of tests for your new code to tests target. And of course, the code should be of general use to more than just a couple of folks. Send your patches to [email protected].

2017.10.23 Version 2.7.4
Added support for explicit transactions.

Add warning that `beginTransaction` and `inTransaction` behavior is likely to change.

2017.10.20 Version 2.7.3
Added support for immediate transactions and checkpoint. (thanks to @benasher44)

Expand Down
2 changes: 1 addition & 1 deletion FMDB.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'FMDB'
s.version = '2.7.3'
s.version = '2.7.4'
s.summary = 'A Cocoa / Objective-C wrapper around SQLite.'
s.homepage = 'https://github.com/ccgus/fmdb'
s.license = 'MIT'
Expand Down
91 changes: 91 additions & 0 deletions Tests/FMDatabaseQueueTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,54 @@ - (void)testURLOpenNoURL {
queue = nil;
}

- (void)testInvalidURL {
NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
NSURL *folderURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
NSURL *fileURL = [folderURL URLByAppendingPathComponent:@"test.sqlite"];

FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithURL:fileURL];
XCTAssertNil(queue, @"Database queue should not be returned for invalid path");
queue = nil;
}

- (void)testInvalidPath {
NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
NSURL *folderURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
NSURL *fileURL = [folderURL URLByAppendingPathComponent:@"test.sqlite"];

FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithPath:fileURL.path];
XCTAssertNil(queue, @"Database queue should not be returned for invalid path");
queue = nil;
}

- (void)testReopenFailure {
NSFileManager *manager = [NSFileManager defaultManager];

NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
NSURL *folderURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
BOOL success = [manager createDirectoryAtURL:folderURL withIntermediateDirectories:true attributes:nil error:nil];
NSAssert(success, @"Unable to create folder");

NSURL *fileURL = [folderURL URLByAppendingPathComponent:@"test.sqlite"];

FMDatabaseQueue *queue = [[FMDatabaseQueue alloc] initWithURL:fileURL];
XCTAssert(queue, @"Database queue was unable to be created");

[queue close];

success = [manager removeItemAtURL:fileURL error:nil];
XCTAssert(success, @"Unable to remove database");

success = [manager removeItemAtURL:folderURL error:nil];
XCTAssert(success, @"Unable to remove folder");

[queue inDatabase:^(FMDatabase *db) {
XCTAssertNil(db, @"Should be `nil` or never have reached here because database couldn't be reopened");
}];

queue = nil;
}

- (void)testURLOpen {
NSURL *tempFolder = [NSURL fileURLWithPath:NSTemporaryDirectory()];
NSURL *fileURL = [tempFolder URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
Expand Down Expand Up @@ -260,7 +308,50 @@ - (void)testTransaction

XCTAssertEqual(rowCount, 2);
}];

}

- (void)testSavePoint
{
[self.queue inDatabase:^(FMDatabase *adb) {
[adb executeUpdate:@"create table transtest (a integer)"];
XCTAssertTrue([adb executeUpdate:@"insert into transtest values (1)"]);
XCTAssertTrue([adb executeUpdate:@"insert into transtest values (2)"]);

int rowCount = 0;
FMResultSet *ars = [adb executeQuery:@"select * from transtest"];
while ([ars next]) {
rowCount++;
}

XCTAssertEqual(rowCount, 2);
}];

[self.queue inSavePoint:^(FMDatabase *adb, BOOL *rollback) {
XCTAssertTrue([adb executeUpdate:@"insert into transtest values (3)"]);

if (YES) {
// uh oh!, something went wrong (not really, this is just a test
*rollback = YES;
return;
}

XCTFail(@"This shouldn't be reached");
}];

[self.queue inDatabase:^(FMDatabase *adb) {

int rowCount = 0;
FMResultSet *ars = [adb executeQuery:@"select * from transtest"];
while ([ars next]) {
rowCount++;
}

XCTAssertFalse([adb hasOpenResultSets]);

XCTAssertEqual(rowCount, 2);
}];

}

@end
2 changes: 1 addition & 1 deletion Tests/FMDatabaseTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -1125,7 +1125,7 @@ - (void)createCustomFunctions {
}

- (void)testVersionNumber {
XCTAssertTrue([FMDatabase FMDBVersion] == 0x0273); // this is going to break everytime we bump it.
XCTAssertTrue([FMDatabase FMDBVersion] == 0x0274); // this is going to break everytime we bump it.
}

- (void)testExecuteStatements {
Expand Down
3 changes: 1 addition & 2 deletions Tests/FMResultSetTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,11 @@ - (void)testNextWithError_WithBusyError
FMDatabase *newDB = [FMDatabase databaseWithPath:self.databasePath];
[newDB open];

[newDB beginTransaction];
[newDB beginExclusiveTransaction];
NSError *error;
XCTAssertFalse([resultSet nextWithError:&error]);
[newDB commit];


XCTAssertEqual(error.code, SQLITE_BUSY, @"SQLITE_BUSY should be the last error");
[resultSet close];
}
Expand Down
14 changes: 13 additions & 1 deletion fmdb.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0820;
LastUpgradeCheck = 0900;
TargetAttributes = {
83C73EFD1C326AB000FFC730 = {
CreatedOnToolsVersion = 7.2;
Expand Down Expand Up @@ -767,12 +767,18 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = 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_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
Expand All @@ -799,12 +805,18 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = 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_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
Expand Down
4 changes: 3 additions & 1 deletion fmdb.xcodeproj/xcshareddata/xcschemes/FMDB MacOS.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand All @@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
Expand All @@ -36,6 +37,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
Expand Down
4 changes: 3 additions & 1 deletion fmdb.xcodeproj/xcshareddata/xcschemes/FMDB iOS.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand All @@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
Expand All @@ -36,6 +37,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
Expand Down
25 changes: 23 additions & 2 deletions src/fmdb/FMDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,15 @@ typedef NS_ENUM(int, FMDBCheckpointMode) {
@see rollback
@see beginDeferredTransaction
@see isInTransaction

@warning Unlike SQLite's `BEGIN TRANSACTION`, this method currently performs
an exclusive transaction, not a deferred transaction. This behavior
is likely to change in future versions of FMDB, whereby this method
will likely eventually adopt standard SQLite behavior and perform
deferred transactions. If you really need exclusive tranaction, it is
recommended that you use `beginExclusiveTransaction`, instead, not
only to make your intent explicit, but also to future-proof your code.

*/

- (BOOL)beginTransaction;
Expand All @@ -702,9 +711,9 @@ typedef NS_ENUM(int, FMDBCheckpointMode) {
- (BOOL)beginDeferredTransaction;

/** Begin an immediate transaction

@return `YES` on success; `NO` on failure. If failed, you can call `<lastError>`, `<lastErrorCode>`, or `<lastErrorMessage>` for diagnostic information regarding the failure.

@see commit
@see rollback
@see beginTransaction
Expand All @@ -713,6 +722,18 @@ typedef NS_ENUM(int, FMDBCheckpointMode) {

- (BOOL)beginImmediateTransaction;

/** Begin an exclusive transaction

@return `YES` on success; `NO` on failure. If failed, you can call `<lastError>`, `<lastErrorCode>`, or `<lastErrorMessage>` for diagnostic information regarding the failure.

@see commit
@see rollback
@see beginTransaction
@see isInTransaction
*/

- (BOOL)beginExclusiveTransaction;

/** Commit a transaction

Commit a transaction that was initiated with either `<beginTransaction>` or with `<beginDeferredTransaction>`.
Expand Down
18 changes: 14 additions & 4 deletions src/fmdb/FMDatabase.m
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ - (NSURL *)databaseURL {
}

+ (NSString*)FMDBUserVersion {
return @"2.7.3";
return @"2.7.4";
}

// returns 0x0240 for version 2.4. This makes it super easy to do things like:
Expand Down Expand Up @@ -1312,6 +1312,16 @@ - (BOOL)commit {
return b;
}

- (BOOL)beginTransaction {

BOOL b = [self executeUpdate:@"begin exclusive transaction"];
if (b) {
_isInTransaction = YES;
}

return b;
}

- (BOOL)beginDeferredTransaction {

BOOL b = [self executeUpdate:@"begin deferred transaction"];
Expand All @@ -1323,16 +1333,16 @@ - (BOOL)beginDeferredTransaction {
}

- (BOOL)beginImmediateTransaction {

BOOL b = [self executeUpdate:@"begin immediate transaction"];
if (b) {
_isInTransaction = YES;
}

return b;
}

- (BOOL)beginTransaction {
- (BOOL)beginExclusiveTransaction {

BOOL b = [self executeUpdate:@"begin exclusive transaction"];
if (b) {
Expand Down
17 changes: 16 additions & 1 deletion src/fmdb/FMDatabasePool.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,26 @@ NS_ASSUME_NONNULL_BEGIN
- (void)inDatabase:(__attribute__((noescape)) void (^)(FMDatabase *db))block;

/** Synchronously perform database operations in pool using transaction.

@param block The code to be run on the `FMDatabasePool` pool.

@warning Unlike SQLite's `BEGIN TRANSACTION`, this method currently performs
an exclusive transaction, not a deferred transaction. This behavior
is likely to change in future versions of FMDB, whereby this method
will likely eventually adopt standard SQLite behavior and perform
deferred transactions. If you really need exclusive tranaction, it is
recommended that you use `inExclusiveTransaction`, instead, not only
to make your intent explicit, but also to future-proof your code.
*/

- (void)inTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block;

/** Synchronously perform database operations in pool using exclusive transaction.

@param block The code to be run on the `FMDatabasePool` pool.
*/

- (void)inTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block;
- (void)inExclusiveTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block;

/** Synchronously perform database operations in pool using deferred transaction.

Expand Down
6 changes: 5 additions & 1 deletion src/fmdb/FMDatabasePool.m
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,15 @@ - (void)beginTransaction:(FMDBTransaction)transaction withBlock:(void (^)(FMData
[self pushDatabaseBackInPool:db];
}

- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
[self beginTransaction:FMDBTransactionExclusive withBlock:block];
}

- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
[self beginTransaction:FMDBTransactionDeferred withBlock:block];
}

- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
- (void)inExclusiveTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
[self beginTransaction:FMDBTransactionExclusive withBlock:block];
}

Expand Down
Loading