浏览代码

Adding 'nextWithError' method to FMResultSet to obtain error details

Roshan Muralidharan 11 年之前
父节点
当前提交
25f34a14f4
共有 4 个文件被更改,包括 134 次插入1 次删除
  1. 91 0
      Tests/FMResultSetTests.m
  2. 4 0
      fmdb.xcodeproj/project.pbxproj
  3. 14 1
      src/fmdb/FMResultSet.h
  4. 25 0
      src/fmdb/FMResultSet.m

+ 91 - 0
Tests/FMResultSetTests.m

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

+ 4 - 0
fmdb.xcodeproj/project.pbxproj

@@ -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; };
@@ -275,6 +277,7 @@
 		BF5D041A18416BB2008C5AA9 /* Tests */ = {
 			isa = PBXGroup;
 			children = (
+				3354379B19E71096005661F3 /* FMResultSetTests.m */,
 				BF940F5A18417D490001E077 /* FMDBTempDBTests.h */,
 				BF940F5B18417D490001E077 /* FMDBTempDBTests.m */,
 				BF5D042018416BB2008C5AA9 /* FMDatabaseTests.m */,
@@ -488,6 +491,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 */,

+ 14 - 1
src/fmdb/FMResultSet.h

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

+ 25 - 0
src/fmdb/FMResultSet.m

@@ -147,26 +147,51 @@ - (NSDictionary*)resultDictionary {
 
 
 - (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];
+        }
     }