Merge branch 'rosh89-FMResultSet_Next_Error_Handling'

This commit is contained in:
August Mueller 2014-10-19 14:35:52 -07:00
commit dca59f3c3b
5 changed files with 144 additions and 6 deletions

View File

@ -3,6 +3,9 @@ Zip, nada, zilch. Got any ideas?
If you would like to contribute some code- awesome! I just ask that you make it conform to the coding conventions already set in here, and to add a couple of tests for your new code to fmdb.m. And of course, the code should be of general use to more than just a couple of folks. Send your patches to gus@flyingmeat.com.
2013.10.19
Added a 'nextWithError:' to FMResultSet. Thanks to Roshan Muralidharan for the patch.
2014.09.10
New classes for exposing SQLite's FTS features. Thanks to Andrew Goodale for the code.

91
Tests/FMResultSetTests.m Normal file
View File

@ -0,0 +1,91 @@
//
// FMResultSetTests.m
// fmdb
//
// Created by Muralidharan,Roshan on 10/6/14.
//
//
#import "FMDBTempDBTests.h"
#import "FMDatabase.h"
#import "FMResultSet.h"
@interface FMResultSetTests : FMDBTempDBTests
@end
@implementation FMResultSetTests
+ (void)populateDatabase:(FMDatabase *)db
{
[db executeUpdate:@"create table test (a text, b text, c integer, d double, e double)"];
[db beginTransaction];
int i = 0;
while (i++ < 20) {
[db executeUpdate:@"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,
@"hi'",
[NSString stringWithFormat:@"number %d", i],
[NSNumber numberWithInt:i],
[NSDate date],
[NSNumber numberWithFloat:2.2f]];
}
[db commit];
}
- (void)testNextWithError_WithoutError
{
[self.db executeUpdate:@"CREATE TABLE testTable(key INTEGER PRIMARY KEY, value INTEGER)"];
[self.db executeUpdate:@"INSERT INTO testTable (key, value) VALUES (1, 2)"];
[self.db executeUpdate:@"INSERT INTO testTable (key, value) VALUES (2, 4)"];
FMResultSet *resultSet = [self.db executeQuery:@"SELECT * FROM testTable WHERE key=1"];
XCTAssertNotNil(resultSet);
NSError *error;
XCTAssertTrue([resultSet nextWithError:&error]);
XCTAssertNil(error);
XCTAssertFalse([resultSet nextWithError:&error]);
XCTAssertNil(error);
[resultSet close];
}
- (void)testNextWithError_WithBusyError
{
[self.db executeUpdate:@"CREATE TABLE testTable(key INTEGER PRIMARY KEY, value INTEGER)"];
[self.db executeUpdate:@"INSERT INTO testTable (key, value) VALUES (1, 2)"];
[self.db executeUpdate:@"INSERT INTO testTable (key, value) VALUES (2, 4)"];
FMResultSet *resultSet = [self.db executeQuery:@"SELECT * FROM testTable WHERE key=1"];
XCTAssertNotNil(resultSet);
FMDatabase *newDB = [FMDatabase databaseWithPath:self.databasePath];
[newDB open];
[newDB beginTransaction];
NSError *error;
XCTAssertFalse([resultSet nextWithError:&error]);
[newDB commit];
XCTAssertEqual(error.code, SQLITE_BUSY, @"SQLITE_BUSY should be the last error");
[resultSet close];
}
- (void)testNextWithError_WithMisuseError
{
[self.db executeUpdate:@"CREATE TABLE testTable(key INTEGER PRIMARY KEY, value INTEGER)"];
[self.db executeUpdate:@"INSERT INTO testTable (key, value) VALUES (1, 2)"];
[self.db executeUpdate:@"INSERT INTO testTable (key, value) VALUES (2, 4)"];
FMResultSet *resultSet = [self.db executeQuery:@"SELECT * FROM testTable WHERE key=9"];
XCTAssertNotNil(resultSet);
XCTAssertFalse([resultSet next]);
NSError *error;
XCTAssertFalse([resultSet nextWithError:&error]);
XCTAssertEqual(error.code, SQLITE_MISUSE, @"SQLITE_MISUSE should be the last error");
}
@end

View File

@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
3354379C19E71096005661F3 /* FMResultSetTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3354379B19E71096005661F3 /* FMResultSetTests.m */; };
621721B21892BFE30006691F /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = CCC24EBB0A13E34D00A6D3E3 /* FMDatabase.m */; };
621721B31892BFE30006691F /* FMResultSet.m in Sources */ = {isa = PBXBuildFile; fileRef = CCC24EC00A13E34D00A6D3E3 /* FMResultSet.m */; };
621721B41892BFE30006691F /* FMDatabaseQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = CC47A00E148581E9002CCDAB /* FMDatabaseQueue.m */; };
@ -88,6 +89,7 @@
/* Begin PBXFileReference section */
08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
32A70AAB03705E1F00C91783 /* fmdb_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fmdb_Prefix.pch; path = src/sample/fmdb_Prefix.pch; sourceTree = SOURCE_ROOT; };
3354379B19E71096005661F3 /* FMResultSetTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMResultSetTests.m; sourceTree = "<group>"; };
6290CBB5188FE836009790F8 /* libFMDB-IOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libFMDB-IOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
6290CBB6188FE836009790F8 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
6290CBC6188FE837009790F8 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
@ -293,13 +295,14 @@
BF5D041A18416BB2008C5AA9 /* Tests */ = {
isa = PBXGroup;
children = (
BF940F5A18417D490001E077 /* FMDBTempDBTests.h */,
BF940F5B18417D490001E077 /* FMDBTempDBTests.m */,
BF5D042018416BB2008C5AA9 /* FMDatabaseTests.m */,
BFE55E121841C9A000CB3A63 /* FMDatabasePoolTests.m */,
BFE55E141841D38800CB3A63 /* FMDatabaseQueueTests.m */,
BF940F5D18417DEA0001E077 /* FMDatabaseAdditionsTests.m */,
67CB1E2F19AD27D000A3CA7F /* FMDatabaseFTS3Tests.m */,
BFE55E121841C9A000CB3A63 /* FMDatabasePoolTests.m */,
BFE55E141841D38800CB3A63 /* FMDatabaseQueueTests.m */,
BF5D042018416BB2008C5AA9 /* FMDatabaseTests.m */,
BF940F5A18417D490001E077 /* FMDBTempDBTests.h */,
BF940F5B18417D490001E077 /* FMDBTempDBTests.m */,
3354379B19E71096005661F3 /* FMResultSetTests.m */,
BF5D041B18416BB2008C5AA9 /* Supporting Files */,
);
path = Tests;
@ -506,6 +509,7 @@
CCA66A3019C0CB1900EFDAC1 /* FMTokenizers.m in Sources */,
BF940F5C18417D490001E077 /* FMDBTempDBTests.m in Sources */,
BF940F5E18417DEA0001E077 /* FMDatabaseAdditionsTests.m in Sources */,
3354379C19E71096005661F3 /* FMResultSetTests.m in Sources */,
BF5D042118416BB2008C5AA9 /* FMDatabaseTests.m in Sources */,
67CB1E3019AD27D000A3CA7F /* FMDatabaseFTS3Tests.m in Sources */,
BFE55E131841C9A000CB3A63 /* FMDatabasePoolTests.m in Sources */,
@ -608,6 +612,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx10.9;
};
name = Debug;
};
@ -627,6 +632,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
SDKROOT = macosx10.9;
};
name = Release;
};

View File

@ -74,7 +74,7 @@
/** Retrieve next row for result set.
You must always invoke `next` before attempting to access the values returned in a query, even if you're only expecting one.
You must always invoke `next` or `nextWithError` before attempting to access the values returned in a query, even if you're only expecting one.
@return `YES` if row successfully retrieved; `NO` if end of result set reached
@ -83,6 +83,19 @@
- (BOOL)next;
/** Retrieve next row for result set.
You must always invoke `next` or `nextWithError` before attempting to access the values returned in a query, even if you're only expecting one.
@param outErr A 'NSError' object to receive any error object (if any).
@return 'YES' if row successfully retrieved; 'NO' if end of result set reached
@see hasAnotherRow
*/
- (BOOL)nextWithError:(NSError **)outErr;
/** Did the last call to `<next>` succeed in retrieving another row?
@return `YES` if the last call to `<next>` succeeded in retrieving another record; `NO` if not.

View File

@ -147,26 +147,51 @@
- (BOOL)next {
return [self nextWithError:nil];
}
- (BOOL)nextWithError:(NSError **)outErr {
int rc = sqlite3_step([_statement statement]);
if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [_parentDB databasePath]);
NSLog(@"Database busy");
if (outErr) {
*outErr = [_parentDB lastError];
}
}
else if (SQLITE_DONE == rc || SQLITE_ROW == rc) {
// all is well, let's return.
}
else if (SQLITE_ERROR == rc) {
NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle]));
if (outErr) {
*outErr = [_parentDB lastError];
}
}
else if (SQLITE_MISUSE == rc) {
// uh oh.
NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle]));
if (outErr) {
if (_parentDB) {
*outErr = [_parentDB lastError];
}
else {
// If 'next' or 'nextWithError' is called after the result set is closed,
// we need to return the appropriate error.
NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:@"parentDB does not exist" forKey:NSLocalizedDescriptionKey];
*outErr = [NSError errorWithDomain:@"FMDatabase" code:SQLITE_MISUSE userInfo:errorMessage];
}
}
}
else {
// wtf?
NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle]));
if (outErr) {
*outErr = [_parentDB lastError];
}
}