Skip to content

Commit

Permalink
Added parameter support.
Browse files Browse the repository at this point in the history
  • Loading branch information
ccgus committed Jul 14, 2011
1 parent 30d4780 commit b6af107
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 36 deletions.
3 changes: 3 additions & 0 deletions CHANGES_AND_TODO_LIST.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 [email protected].

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.
Expand Down
11 changes: 10 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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):

Expand Down
4 changes: 2 additions & 2 deletions src/FMDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
132 changes: 100 additions & 32 deletions src/FMDatabase.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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];
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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, ... {
Expand All @@ -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;
Expand Down
6 changes: 5 additions & 1 deletion src/FMDatabaseAdditions.m
Original file line number Diff line number Diff line change
Expand Up @@ -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]; \
Expand Down
50 changes: 50 additions & 0 deletions src/fmdb.m
Original file line number Diff line number Diff line change
Expand Up @@ -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]) {
Expand Down

0 comments on commit b6af107

Please sign in to comment.