Bläddra i källkod

Added parameter support.

August Mueller 14 år sedan
förälder
incheckning
b6af107dbb
6 ändrade filer med 170 tillägg och 36 borttagningar
  1. 3 0
      CHANGES_AND_TODO_LIST.txt
  2. 10 1
      README.markdown
  3. 2 2
      src/FMDatabase.h
  4. 100 32
      src/FMDatabase.m
  5. 5 1
      src/FMDatabaseAdditions.m
  6. 50 0
      src/fmdb.m

+ 3 - 0
CHANGES_AND_TODO_LIST.txt

@@ -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.
 
+2011.07.14:
+    Added methods for named parameters, using keys from an NSDictionary (Thanks to Drarok Ithaqua for the patches!)
+
 2011.06.22
     Changed some methods to properties.  Hello 2011.
     Added a warning when you try and use a database that wasn't opened.  Hacked together based on patches from Drarok Ithaqua.

+ 10 - 1
README.markdown

@@ -87,7 +87,16 @@ When providing a SQL statement to FMDB, you should not attempt to "sanitize" any
 
 	INSERT INTO myTable VALUES (?, ?, ?)
 	
-The `?` character is recognized by SQLite as a placeholder for a value to be inserted.  The execution methods all accept a variable number of arguments (or a representation of those arguments, such as an `NSArray` or a `va_list`), which are properly escaped for you.
+The `?` character is recognized by SQLite as a placeholder for a value to be inserted.  The execution methods all accept a variable number of arguments (or a representation of those arguments, such as an `NSArray`, `NSDictionary`, or a `va_list`), which are properly escaped for you.
+
+Alternatively, you may use named parameters syntax:
+
+    INSERT INTO myTable VALUES (:id, :name, :value)
+    
+The parameters *must* start with a colon. SQLite itself supports other characters, but internally the Dictionary keys are prefixed with a colon, do **not** include the colon in your dictionary keys.
+
+    NSDictionary *argsDict = [NSDictionary dictionaryWithObjectsAndKeys:@"My Name", @"name", nil];
+    [db executeUpdate:@"INSERT INTO myTable (name) VALUES (:name)" withArgumentsInDictionary:argsDict];
 
 Thus, you SHOULD NOT do this (or anything like this):
 

+ 2 - 2
src/FMDatabase.h

@@ -65,12 +65,12 @@
 - (BOOL)executeUpdate:(NSString*)sql, ...;
 - (BOOL)executeUpdateWithFormat:(NSString *)format, ...;
 - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments;
-- (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args; // you shouldn't ever need to call this.  use the previous two instead.
+- (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments;
 
 - (FMResultSet *)executeQuery:(NSString*)sql, ...;
 - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...;
 - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments;
-- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args; // you shouldn't ever need to call this.  use the previous two instead.
+- (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments;
 
 - (BOOL)rollback;
 - (BOOL)commit;

+ 100 - 32
src/FMDatabase.m

@@ -4,7 +4,8 @@
 @interface FMDatabase ()
 
 - (void)checkPoolPushBack;
-
+- (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;
 @end
 
 @implementation FMDatabase
@@ -466,7 +467,11 @@ - (void)extractSQL:(NSString *)sql argumentsList:(va_list)args intoString:(NSMut
     }
 }
 
-- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args {
+- (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments {
+    return [self executeQuery:sql withArgumentsInArray:nil orDictionary:arguments orVAList:nil];
+}
+
+- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
     
     if (![self databaseExists]) {
         return 0x00;
@@ -540,22 +545,52 @@ - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arr
     int idx = 0;
     int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!)
     
-    while (idx < queryCount) {
+    // If dictionaryArgs is passed in, that means we are using sqlite's named parameter support
+    if (dictionaryArgs) {
         
-        if (arrayArgs) {
-            obj = [arrayArgs objectAtIndex:idx];
-        }
-        else {
-            obj = va_arg(args, id);
+        for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
+            
+            // Prefix the key with a colon.
+            NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
+            
+            // Get the index for the parameter name.
+            int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
+            
+            NSLog(@"namedIdx: %d", namedIdx);
+            NSLog(@"%@", [dictionaryArgs objectForKey:dictionaryKey]);
+            [parameterName release];
+            
+            if (namedIdx > 0) {
+                // Standard binding from here.
+                [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
+            }
+            else {
+                NSLog(@"Could not find index for %@", dictionaryKey);
+            }
         }
         
-        if (_traceExecution) {
-            NSLog(@"obj: %@", obj);
+        // we need the count of params to avoid an error below.
+        idx = (int) [[dictionaryArgs allKeys] count];
+    }
+    else {
+            
+        while (idx < queryCount) {
+            
+            if (arrayArgs) {
+                obj = [arrayArgs objectAtIndex:idx];
+            }
+            else {
+                obj = va_arg(args, id);
+            }
+            
+            if (_traceExecution) {
+                NSLog(@"obj: %@", obj);
+            }
+            
+            idx++;
+            
+            [self bindObject:obj toColumn:idx inStatement:pStmt];
         }
-        
-        idx++;
-        
-        [self bindObject:obj toColumn:idx inStatement:pStmt];
     }
     
     if (idx != queryCount) {
@@ -597,7 +632,7 @@ - (FMResultSet *)executeQuery:(NSString*)sql, ... {
     va_list args;
     va_start(args, sql);
     
-    id result = [self executeQuery:sql withArgumentsInArray:nil orVAList:args];
+    id result = [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args];
     
     va_end(args);
     return result;
@@ -617,10 +652,10 @@ - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... {
 }
 
 - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments {
-    return [self executeQuery:sql withArgumentsInArray:arguments orVAList:nil];
+    return [self executeQuery:sql withArgumentsInArray:arguments orDictionary:nil orVAList:nil];
 }
 
-- (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args {
+- (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
     
     if (![self databaseExists]) {
         [self checkPoolPushBack];
@@ -699,24 +734,53 @@ - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArra
     int idx = 0;
     int queryCount = sqlite3_bind_parameter_count(pStmt);
     
-    while (idx < queryCount) {
+    // If dictionaryArgs is passed in, that means we are using sqlite's named parameter support
+    if (dictionaryArgs) {
         
-        if (arrayArgs) {
-            obj = [arrayArgs objectAtIndex:idx];
-        }
-        else {
-            obj = va_arg(args, id);
-        }
-        
-        if (_traceExecution) {
-            NSLog(@"obj: %@", obj);
+        for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
+            
+            // Prefix the key with a colon.
+            NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
+            
+            // Get the index for the parameter name.
+            int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
+            
+            [parameterName release];
+            
+            if (namedIdx > 0) {
+                // Standard binding from here.
+                [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
+            }
+            else {
+                NSLog(@"Could not find index for %@", dictionaryKey);
+            }
         }
         
-        idx++;
+        // we need the count of params to avoid an error below.
+        idx = (int) [[dictionaryArgs allKeys] count];
+    }
+    else {
         
-        [self bindObject:obj toColumn:idx inStatement:pStmt];
+        while (idx < queryCount) {
+            
+            if (arrayArgs) {
+                obj = [arrayArgs objectAtIndex:idx];
+            }
+            else {
+                obj = va_arg(args, id);
+            }
+            
+            if (_traceExecution) {
+                NSLog(@"obj: %@", obj);
+            }
+            
+            idx++;
+            
+            [self bindObject:obj toColumn:idx inStatement:pStmt];
+        }
     }
     
+    
     if (idx != queryCount) {
         NSLog(@"Error: the bind count is not correct for the # of variables (%@) (executeUpdate)", sql);
         sqlite3_finalize(pStmt);
@@ -806,14 +870,18 @@ - (BOOL)executeUpdate:(NSString*)sql, ... {
     va_list args;
     va_start(args, sql);
     
-    BOOL result = [self executeUpdate:sql error:nil withArgumentsInArray:nil orVAList:args];
+    BOOL result = [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:nil orVAList:args];
     
     va_end(args);
     return result;
 }
 
 - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments {
-    return [self executeUpdate:sql error:nil withArgumentsInArray:arguments orVAList:nil];
+    return [self executeUpdate:sql error:nil withArgumentsInArray:arguments orDictionary:nil orVAList:nil];
+}
+
+- (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments {
+    return [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:arguments orVAList:nil];
 }
 
 - (BOOL)executeUpdateWithFormat:(NSString*)format, ... {
@@ -834,7 +902,7 @@ - (BOOL)update:(NSString*)sql error:(NSError**)outErr bind:(id)bindArgs, ... {
     va_list args;
     va_start(args, bindArgs);
     
-    BOOL result = [self executeUpdate:sql error:outErr withArgumentsInArray:nil orVAList:args];
+    BOOL result = [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:args];
     
     va_end(args);
     return result;

+ 5 - 1
src/FMDatabaseAdditions.m

@@ -9,12 +9,16 @@
 #import "FMDatabase.h"
 #import "FMDatabaseAdditions.h"
 
+@interface FMDatabase (PrivateStuff)
+- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args;
+@end
+
 @implementation FMDatabase (FMDatabaseAdditions)
 
 #define RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(type, sel)             \
 va_list args;                                                        \
 va_start(args, query);                                               \
-FMResultSet *resultSet = [self executeQuery:query withArgumentsInArray:0x00 orVAList:args];   \
+FMResultSet *resultSet = [self executeQuery:query withArgumentsInArray:0x00 orDictionary:0x00 orVAList:args];   \
 va_end(args);                                                        \
 if (![resultSet next]) { return (type)0; }                           \
 type ret = [resultSet sel:0];                                        \

+ 50 - 0
src/fmdb.m

@@ -499,6 +499,56 @@ int main (int argc, const char * argv[]) {
     
     
     
+    
+    {
+        // -------------------------------------------------------------------------------
+        // Named parameters.
+        FMDBQuickCheck([db executeUpdate:@"create table namedparamtest (a text, b text, c integer, d double)"]);
+        NSMutableDictionary *dictionaryArgs = [NSMutableDictionary dictionary];
+        [dictionaryArgs setObject:@"Text1" forKey:@"a"];
+        [dictionaryArgs setObject:@"Text2" forKey:@"b"];
+        [dictionaryArgs setObject:[NSNumber numberWithInt:1] forKey:@"c"];
+        [dictionaryArgs setObject:[NSNumber numberWithDouble:2.0] forKey:@"d"];
+        FMDBQuickCheck([db executeUpdate:@"insert into namedparamtest values (:a, :b, :c, :d)" withParameterDictionary:dictionaryArgs]);
+        
+        rs = [db executeQuery:@"select * from namedparamtest"];
+        
+        FMDBQuickCheck((rs != nil));
+        
+        [rs next];
+        
+        FMDBQuickCheck([[rs stringForColumn:@"a"] isEqualToString:@"Text1"]);
+        FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"Text2"]);
+        FMDBQuickCheck([rs intForColumn:@"c"] == 1);
+        FMDBQuickCheck([rs doubleForColumn:@"d"] == 2.0);
+        
+        [rs close];
+        
+        
+        dictionaryArgs = [NSMutableDictionary dictionary];
+        
+        [dictionaryArgs setObject:@"Text2" forKey:@"blah"];
+        
+        rs = [db executeQuery:@"select * from namedparamtest where b = :blah" withParameterDictionary:dictionaryArgs];
+        
+        FMDBQuickCheck((rs != nil));
+        FMDBQuickCheck([rs next]);
+        FMDBQuickCheck([[rs stringForColumn:@"b"] isEqualToString:@"Text2"]);
+        
+        [rs close];
+        
+        
+        
+        
+    }
+    
+    
+    
+    
+    
+    
+    
+    
     // just for fun.
     rs = [db executeQuery:@"PRAGMA database_list"];
     while ([rs next]) {