Merge pull request #620 from robertmryan/master

Update transactions
This commit is contained in:
August "Gus" Mueller 2017-10-24 09:30:13 -07:00 committed by GitHub
commit e0fcde9a0e
15 changed files with 200 additions and 19 deletions

View File

@ -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 gus@flyingmeat.com.
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)

View File

@ -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'

View File

@ -59,6 +59,54 @@
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]];
@ -260,7 +308,50 @@
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

View File

@ -1125,7 +1125,7 @@
}
- (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 {

View File

@ -69,12 +69,11 @@
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];
}

View File

@ -552,7 +552,7 @@
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0820;
LastUpgradeCheck = 0900;
TargetAttributes = {
83C73EFD1C326AB000FFC730 = {
CreatedOnToolsVersion = 7.2;
@ -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;
@ -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;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
@ -36,6 +37,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
LastUpgradeVersion = "0900"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
@ -36,6 +37,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"

View File

@ -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;
@ -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
@ -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>`.

View File

@ -98,7 +98,7 @@ NS_ASSUME_NONNULL_END
}
+ (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:
@ -1312,6 +1312,16 @@ int FMDBExecuteBulkSQLCallback(void *theBlockAsVoid, int columns, char **values,
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"];
@ -1323,16 +1333,16 @@ int FMDBExecuteBulkSQLCallback(void *theBlockAsVoid, int columns, char **values,
}
- (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) {

View File

@ -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.

View File

@ -281,11 +281,15 @@ typedef NS_ENUM(NSInteger, FMDBTransaction) {
[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];
}

View File

@ -205,17 +205,33 @@ NS_ASSUME_NONNULL_BEGIN
/** Synchronously perform database operations on queue, using transactions.
@param block The code to be run on the queue of `FMDatabaseQueue`
@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 on queue, using deferred transactions.
@param block The code to be run on the queue of `FMDatabaseQueue`
*/
- (void)inDeferredTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block;
/** Synchronously perform database operations on queue, using exclusive transactions.
@param block The code to be run on the queue of `FMDatabaseQueue`
*/
- (void)inExclusiveTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block;
/** Synchronously perform database operations on queue, using immediate transactions.
@param block The code to be run on the queue of `FMDatabaseQueue`

View File

@ -127,7 +127,6 @@ static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey
return [self initWithPath:nil];
}
- (void)dealloc {
FMDBRelease(_db);
FMDBRelease(_path);
@ -189,6 +188,7 @@ static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey
dispatch_sync(_queue, ^() {
FMDatabase *db = [self database];
block(db);
if ([db hasOpenResultSets]) {
@ -238,11 +238,15 @@ static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey
FMDBRelease(self);
}
- (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];
}

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.7.3</string>
<string>2.7.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>