Adding fmdb.

This commit is contained in:
Samuel Clay 2013-05-31 15:31:40 -07:00
parent 8213b91b13
commit c16b6dec2a
12 changed files with 4249 additions and 0 deletions

View file

@ -270,6 +270,12 @@
FF688E5316E6B8D0003B7B42 /* traverse_background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF688E5116E6B8D0003B7B42 /* traverse_background@2x.png */; };
FF6A233216448E0700E15989 /* StoryPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = FF6A233116448E0700E15989 /* StoryPageControl.m */; };
FF6A23391644957800E15989 /* StoryPageControl.xib in Resources */ = {isa = PBXBuildFile; fileRef = FF6A23361644903900E15989 /* StoryPageControl.xib */; };
FF753CCE175858FC00344EC9 /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = FF753CC4175858FC00344EC9 /* FMDatabase.m */; };
FF753CCF175858FC00344EC9 /* FMDatabaseAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = FF753CC6175858FC00344EC9 /* FMDatabaseAdditions.m */; };
FF753CD0175858FC00344EC9 /* FMDatabasePool.m in Sources */ = {isa = PBXBuildFile; fileRef = FF753CC8175858FC00344EC9 /* FMDatabasePool.m */; };
FF753CD1175858FC00344EC9 /* FMDatabaseQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = FF753CCA175858FC00344EC9 /* FMDatabaseQueue.m */; };
FF753CD2175858FC00344EC9 /* fmdb.m in Sources */ = {isa = PBXBuildFile; fileRef = FF753CCB175858FC00344EC9 /* fmdb.m */; };
FF753CD3175858FC00344EC9 /* FMResultSet.m in Sources */ = {isa = PBXBuildFile; fileRef = FF753CCD175858FC00344EC9 /* FMResultSet.m */; };
FF793E1B13F1A9F700F282D2 /* ASIDataCompressor.m in Sources */ = {isa = PBXBuildFile; fileRef = FF793E1813F1A9F700F282D2 /* ASIDataCompressor.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FF793E1C13F1A9F700F282D2 /* ASIDataDecompressor.m in Sources */ = {isa = PBXBuildFile; fileRef = FF793E1A13F1A9F700F282D2 /* ASIDataDecompressor.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FF8364BB1755759A008F5C58 /* traverse_text.png in Resources */ = {isa = PBXBuildFile; fileRef = FF8364B91755759A008F5C58 /* traverse_text.png */; };
@ -800,6 +806,17 @@
FF6A233016448E0700E15989 /* StoryPageControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StoryPageControl.h; sourceTree = "<group>"; };
FF6A233116448E0700E15989 /* StoryPageControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StoryPageControl.m; sourceTree = "<group>"; };
FF6A23361644903900E15989 /* StoryPageControl.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = StoryPageControl.xib; path = ../Classes/StoryPageControl.xib; sourceTree = "<group>"; };
FF753CC3175858FC00344EC9 /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabase.h; sourceTree = "<group>"; };
FF753CC4175858FC00344EC9 /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabase.m; sourceTree = "<group>"; };
FF753CC5175858FC00344EC9 /* FMDatabaseAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseAdditions.h; sourceTree = "<group>"; };
FF753CC6175858FC00344EC9 /* FMDatabaseAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseAdditions.m; sourceTree = "<group>"; };
FF753CC7175858FC00344EC9 /* FMDatabasePool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabasePool.h; sourceTree = "<group>"; };
FF753CC8175858FC00344EC9 /* FMDatabasePool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabasePool.m; sourceTree = "<group>"; };
FF753CC9175858FC00344EC9 /* FMDatabaseQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseQueue.h; sourceTree = "<group>"; };
FF753CCA175858FC00344EC9 /* FMDatabaseQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseQueue.m; sourceTree = "<group>"; };
FF753CCB175858FC00344EC9 /* fmdb.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = fmdb.m; sourceTree = "<group>"; };
FF753CCC175858FC00344EC9 /* FMResultSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMResultSet.h; sourceTree = "<group>"; };
FF753CCD175858FC00344EC9 /* FMResultSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMResultSet.m; sourceTree = "<group>"; };
FF793E1713F1A9F700F282D2 /* ASIDataCompressor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIDataCompressor.h; sourceTree = "<group>"; };
FF793E1813F1A9F700F282D2 /* ASIDataCompressor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASIDataCompressor.m; sourceTree = "<group>"; };
FF793E1913F1A9F700F282D2 /* ASIDataDecompressor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIDataDecompressor.h; sourceTree = "<group>"; };
@ -1137,6 +1154,7 @@
29B97315FDCFA39411CA2CEA /* Other Sources */ = {
isa = PBXGroup;
children = (
FF753CC2175858FC00344EC9 /* fmdb */,
FFDC9F9616E8064C000D8E0C /* ShareThis */,
FF22FE5316E53ADC0046165A /* Underscore */,
43A4BABE15C8663600F3B8D4 /* Popover */,
@ -1779,6 +1797,25 @@
path = Reachability;
sourceTree = "<group>";
};
FF753CC2175858FC00344EC9 /* fmdb */ = {
isa = PBXGroup;
children = (
FF753CC3175858FC00344EC9 /* FMDatabase.h */,
FF753CC4175858FC00344EC9 /* FMDatabase.m */,
FF753CC5175858FC00344EC9 /* FMDatabaseAdditions.h */,
FF753CC6175858FC00344EC9 /* FMDatabaseAdditions.m */,
FF753CC7175858FC00344EC9 /* FMDatabasePool.h */,
FF753CC8175858FC00344EC9 /* FMDatabasePool.m */,
FF753CC9175858FC00344EC9 /* FMDatabaseQueue.h */,
FF753CCA175858FC00344EC9 /* FMDatabaseQueue.m */,
FF753CCB175858FC00344EC9 /* fmdb.m */,
FF753CCC175858FC00344EC9 /* FMResultSet.h */,
FF753CCD175858FC00344EC9 /* FMResultSet.m */,
);
name = fmdb;
path = "Other Sources/fmdb";
sourceTree = "<group>";
};
FF9B565E16E810900054BF68 /* Assets */ = {
isa = PBXGroup;
children = (
@ -2500,6 +2537,12 @@
FFDCA0BC16E80877000D8E0C /* NSData+Base64.m in Sources */,
FFECD016172B0D4200D45A62 /* SafariService.m in Sources */,
FFECD01D172B10BD00D45A62 /* SafariActivityItem.m in Sources */,
FF753CCE175858FC00344EC9 /* FMDatabase.m in Sources */,
FF753CCF175858FC00344EC9 /* FMDatabaseAdditions.m in Sources */,
FF753CD0175858FC00344EC9 /* FMDatabasePool.m in Sources */,
FF753CD1175858FC00344EC9 /* FMDatabaseQueue.m in Sources */,
FF753CD2175858FC00344EC9 /* fmdb.m in Sources */,
FF753CD3175858FC00344EC9 /* FMResultSet.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View file

@ -0,0 +1,218 @@
#import <Foundation/Foundation.h>
#import "sqlite3.h"
#import "FMResultSet.h"
#import "FMDatabasePool.h"
#if ! __has_feature(objc_arc)
#define FMDBAutorelease(__v) ([__v autorelease]);
#define FMDBReturnAutoreleased FMDBAutorelease
#define FMDBRetain(__v) ([__v retain]);
#define FMDBReturnRetained FMDBRetain
#define FMDBRelease(__v) ([__v release]);
#define FMDBDispatchQueueRelease(__v) (dispatch_release(__v));
#else
// -fobjc-arc
#define FMDBAutorelease(__v)
#define FMDBReturnAutoreleased(__v) (__v)
#define FMDBRetain(__v)
#define FMDBReturnRetained(__v) (__v)
#define FMDBRelease(__v)
#if TARGET_OS_IPHONE
// Compiling for iOS
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000
// iOS 6.0 or later
#define FMDBDispatchQueueRelease(__v)
#else
// iOS 5.X or earlier
#define FMDBDispatchQueueRelease(__v) (dispatch_release(__v));
#endif
#else
// Compiling for Mac OS X
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
// Mac OS X 10.8 or later
#define FMDBDispatchQueueRelease(__v)
#else
// Mac OS X 10.7 or earlier
#define FMDBDispatchQueueRelease(__v) (dispatch_release(__v));
#endif
#endif
#endif
@interface FMDatabase : NSObject {
sqlite3* _db;
NSString* _databasePath;
BOOL _logsErrors;
BOOL _crashOnErrors;
BOOL _traceExecution;
BOOL _checkedOut;
BOOL _shouldCacheStatements;
BOOL _isExecutingStatement;
BOOL _inTransaction;
int _busyRetryTimeout;
NSMutableDictionary *_cachedStatements;
NSMutableSet *_openResultSets;
NSMutableSet *_openFunctions;
NSDateFormatter *_dateFormat;
}
@property (atomic, assign) BOOL traceExecution;
@property (atomic, assign) BOOL checkedOut;
@property (atomic, assign) int busyRetryTimeout;
@property (atomic, assign) BOOL crashOnErrors;
@property (atomic, assign) BOOL logsErrors;
@property (atomic, retain) NSMutableDictionary *cachedStatements;
+ (id)databaseWithPath:(NSString*)inPath;
- (id)initWithPath:(NSString*)inPath;
- (BOOL)open;
#if SQLITE_VERSION_NUMBER >= 3005000
- (BOOL)openWithFlags:(int)flags;
#endif
- (BOOL)close;
- (BOOL)goodConnection;
- (void)clearCachedStatements;
- (void)closeOpenResultSets;
- (BOOL)hasOpenResultSets;
// encryption methods. You need to have purchased the sqlite encryption extensions for these to work.
- (BOOL)setKey:(NSString*)key;
- (BOOL)rekey:(NSString*)key;
- (BOOL)setKeyWithData:(NSData *)keyData;
- (BOOL)rekeyWithData:(NSData *)keyData;
- (NSString *)databasePath;
- (NSString*)lastErrorMessage;
- (int)lastErrorCode;
- (BOOL)hadError;
- (NSError*)lastError;
- (sqlite_int64)lastInsertRowId;
- (sqlite3*)sqliteHandle;
- (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ...;
- (BOOL)executeUpdate:(NSString*)sql, ...;
- (BOOL)executeUpdateWithFormat:(NSString *)format, ...;
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments;
- (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 withParameterDictionary:(NSDictionary *)arguments;
- (BOOL)rollback;
- (BOOL)commit;
- (BOOL)beginTransaction;
- (BOOL)beginDeferredTransaction;
- (BOOL)inTransaction;
- (BOOL)shouldCacheStatements;
- (void)setShouldCacheStatements:(BOOL)value;
#if SQLITE_VERSION_NUMBER >= 3007000
- (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr;
- (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr;
- (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr;
- (NSError*)inSavePoint:(void (^)(BOOL *rollback))block;
#endif
+ (BOOL)isSQLiteThreadSafe;
+ (NSString*)sqliteLibVersion;
- (int)changes;
- (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(sqlite3_context *context, int argc, sqlite3_value **argv))block;
/** Generate an NSDateFormat that won't be broken by timezone or locale changes.
Use this method to generate values to set the dateFormat property.
@param dateFormat A valid NSDateFormatter format string.
Example:
myDB.dateFormat = [FMDatabase storeableDateFormat:@"yyyy-MM-dd HH:mm:ss"];
Note that NSDateFormatter is not thread-safe, so the formatter generated by this method should be assigned to only one FMDB instance and should not be used for other purposes.
*/
+ (NSDateFormatter *)storeableDateFormat:(NSString *)format;
/** Test whether the database has a date formatter assigned.
*/
- (BOOL)hasDateFormatter;
/** Set to a date formatter to use string dates with sqlite instead of the default UNIX timestamps.
Set to nil to use UNIX timestamps.
Defaults to nil.
Should be set using a formatter generated using FMDatabase::storeableDateFormat.
Note there is no direct getter for the NSDateFormatter, and you should not use the formatter you pass to FMDB for other purposes, as NSDateFormatter is not thread-safe.
*/
- (void)setDateFormat:(NSDateFormatter *)format;
/** Convert the supplied NSString to NSDate, using the current database formatter.
Returns nil if no formatter is set.
*/
- (NSDate *)dateFromString:(NSString *)s;
/** Convert the supplied NSDate to NSString, using the current database formatter.
Returns nil if no formatter is set.
*/
- (NSString *)stringFromDate:(NSDate *)date;
@end
@interface FMStatement : NSObject {
sqlite3_stmt *_statement;
NSString *_query;
long _useCount;
}
@property (atomic, assign) long useCount;
@property (atomic, retain) NSString *query;
@property (atomic, assign) sqlite3_stmt *statement;
- (void)close;
- (void)reset;
@end

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,47 @@
//
// FMDatabaseAdditions.h
// fmkit
//
// Created by August Mueller on 10/30/05.
// Copyright 2005 Flying Meat Inc.. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface FMDatabase (FMDatabaseAdditions)
- (int)intForQuery:(NSString*)objs, ...;
- (long)longForQuery:(NSString*)objs, ...;
- (BOOL)boolForQuery:(NSString*)objs, ...;
- (double)doubleForQuery:(NSString*)objs, ...;
- (NSString*)stringForQuery:(NSString*)objs, ...;
- (NSData*)dataForQuery:(NSString*)objs, ...;
- (NSDate*)dateForQuery:(NSString*)objs, ...;
// Notice that there's no dataNoCopyForQuery:.
// That would be a bad idea, because we close out the result set, and then what
// happens to the data that we just didn't copy? Who knows, not I.
- (BOOL)tableExists:(NSString*)tableName;
- (FMResultSet*)getSchema;
- (FMResultSet*)getTableSchema:(NSString*)tableName;
- (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName;
- (BOOL)validateSQL:(NSString*)sql error:(NSError**)error;
#if SQLITE_VERSION_NUMBER >= 3007017
- (uint32_t)applicationID;
- (void)setApplicationID:(uint32_t)appID;
- (NSString*)applicationIDString;
- (void)setApplicationIDString:(NSString*)s;
#endif
// deprecated - use columnExists:inTableWithName: instead.
- (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated));
@end

View file

@ -0,0 +1,209 @@
//
// FMDatabaseAdditions.m
// fmkit
//
// Created by August Mueller on 10/30/05.
// Copyright 2005 Flying Meat Inc.. All rights reserved.
//
#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 orDictionary:0x00 orVAList:args]; \
va_end(args); \
if (![resultSet next]) { return (type)0; } \
type ret = [resultSet sel:0]; \
[resultSet close]; \
[resultSet setParentDB:nil]; \
return ret;
- (NSString*)stringForQuery:(NSString*)query, ... {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex);
}
- (int)intForQuery:(NSString*)query, ... {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(int, intForColumnIndex);
}
- (long)longForQuery:(NSString*)query, ... {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(long, longForColumnIndex);
}
- (BOOL)boolForQuery:(NSString*)query, ... {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(BOOL, boolForColumnIndex);
}
- (double)doubleForQuery:(NSString*)query, ... {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(double, doubleForColumnIndex);
}
- (NSData*)dataForQuery:(NSString*)query, ... {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSData *, dataForColumnIndex);
}
- (NSDate*)dateForQuery:(NSString*)query, ... {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSDate *, dateForColumnIndex);
}
- (BOOL)tableExists:(NSString*)tableName {
tableName = [tableName lowercaseString];
FMResultSet *rs = [self executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableName];
//if at least one next exists, table exists
BOOL returnBool = [rs next];
//close and free object
[rs close];
return returnBool;
}
/*
get table with list of tables: result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING]
check if table exist in database (patch from OZLB)
*/
- (FMResultSet*)getSchema {
//result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING]
FMResultSet *rs = [self executeQuery:@"SELECT type, name, tbl_name, rootpage, sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type != 'meta' AND name NOT LIKE 'sqlite_%' ORDER BY tbl_name, type DESC, name"];
return rs;
}
/*
get table schema: result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER]
*/
- (FMResultSet*)getTableSchema:(NSString*)tableName {
//result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER]
FMResultSet *rs = [self executeQuery:[NSString stringWithFormat: @"PRAGMA table_info('%@')", tableName]];
return rs;
}
- (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName {
BOOL returnBool = NO;
tableName = [tableName lowercaseString];
columnName = [columnName lowercaseString];
FMResultSet *rs = [self getTableSchema:tableName];
//check if column is present in table schema
while ([rs next]) {
if ([[[rs stringForColumn:@"name"] lowercaseString] isEqualToString:columnName]) {
returnBool = YES;
break;
}
}
//If this is not done FMDatabase instance stays out of pool
[rs close];
return returnBool;
}
#if SQLITE_VERSION_NUMBER >= 3007017
- (uint32_t)applicationID {
uint32_t r = 0;
FMResultSet *rs = [self executeQuery:@"pragma application_id"];
if ([rs next]) {
r = (uint32_t)[rs longLongIntForColumnIndex:0];
}
[rs close];
return r;
}
- (NSString*)applicationIDString {
NSString *s = NSFileTypeForHFSTypeCode([self applicationID]);
assert([s length] == 6);
s = [s substringWithRange:NSMakeRange(1, 4)];
return s;
}
- (void)setApplicationID:(uint32_t)appID {
NSString *query = [NSString stringWithFormat:@"PRAGMA application_id=%d", appID];
FMResultSet *rs = [self executeQuery:query];
[rs next];
[rs close];
}
- (void)setApplicationIDString:(NSString*)s {
if ([s length] != 4) {
NSLog(@"setApplicationIDString: string passed is not exactly 4 chars long. (was %ld)", [s length]);
}
[self setApplicationID:NSHFSTypeCodeFromFileType([NSString stringWithFormat:@"'%@'", s])];
}
#endif
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
- (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)) {
return [self columnExists:columnName inTableWithName:tableName];
}
#pragma clang diagnostic pop
- (BOOL)validateSQL:(NSString*)sql error:(NSError**)error {
sqlite3_stmt *pStmt = NULL;
BOOL validationSucceeded = YES;
BOOL keepTrying = YES;
int numberOfRetries = 0;
while (keepTrying == YES) {
keepTrying = NO;
int rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {
keepTrying = YES;
usleep(20);
if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) {
NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
NSLog(@"Database busy");
}
}
else if (rc != SQLITE_OK) {
validationSucceeded = NO;
if (error) {
*error = [NSError errorWithDomain:NSCocoaErrorDomain
code:[self lastErrorCode]
userInfo:[NSDictionary dictionaryWithObject:[self lastErrorMessage]
forKey:NSLocalizedDescriptionKey]];
}
}
}
sqlite3_finalize(pStmt);
return validationSucceeded;
}
@end

View file

@ -0,0 +1,75 @@
//
// FMDatabasePool.h
// fmdb
//
// Created by August Mueller on 6/22/11.
// Copyright 2011 Flying Meat Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "sqlite3.h"
/*
***README OR SUFFER***
Before using FMDatabasePool, please consider using FMDatabaseQueue instead.
If you really really really know what you're doing and FMDatabasePool is what
you really really need (ie, you're using a read only database), OK you can use
it. But just be careful not to deadlock!
For an example on deadlocking, search for:
ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD
in the main.m file.
*/
@class FMDatabase;
@interface FMDatabasePool : NSObject {
NSString *_path;
dispatch_queue_t _lockQueue;
NSMutableArray *_databaseInPool;
NSMutableArray *_databaseOutPool;
__unsafe_unretained id _delegate;
NSUInteger _maximumNumberOfDatabasesToCreate;
}
@property (atomic, retain) NSString *path;
@property (atomic, assign) id delegate;
@property (atomic, assign) NSUInteger maximumNumberOfDatabasesToCreate;
+ (id)databasePoolWithPath:(NSString*)aPath;
- (id)initWithPath:(NSString*)aPath;
- (NSUInteger)countOfCheckedInDatabases;
- (NSUInteger)countOfCheckedOutDatabases;
- (NSUInteger)countOfOpenDatabases;
- (void)releaseAllDatabases;
- (void)inDatabase:(void (^)(FMDatabase *db))block;
- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block;
- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block;
#if SQLITE_VERSION_NUMBER >= 3007000
// NOTE: you can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock.
// If you need to nest, use FMDatabase's startSavePointWithName:error: instead.
- (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block;
#endif
@end
@interface NSObject (FMDatabasePoolDelegate)
- (BOOL)databasePool:(FMDatabasePool*)pool shouldAddDatabaseToPool:(FMDatabase*)database;
@end

View file

@ -0,0 +1,244 @@
//
// FMDatabasePool.m
// fmdb
//
// Created by August Mueller on 6/22/11.
// Copyright 2011 Flying Meat Inc. All rights reserved.
//
#import "FMDatabasePool.h"
#import "FMDatabase.h"
@interface FMDatabasePool()
- (void)pushDatabaseBackInPool:(FMDatabase*)db;
- (FMDatabase*)db;
@end
@implementation FMDatabasePool
@synthesize path=_path;
@synthesize delegate=_delegate;
@synthesize maximumNumberOfDatabasesToCreate=_maximumNumberOfDatabasesToCreate;
+ (id)databasePoolWithPath:(NSString*)aPath {
return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]);
}
- (id)initWithPath:(NSString*)aPath {
self = [super init];
if (self != nil) {
_path = [aPath copy];
_lockQueue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);
_databaseInPool = FMDBReturnRetained([NSMutableArray array]);
_databaseOutPool = FMDBReturnRetained([NSMutableArray array]);
}
return self;
}
- (void)dealloc {
_delegate = 0x00;
FMDBRelease(_path);
FMDBRelease(_databaseInPool);
FMDBRelease(_databaseOutPool);
if (_lockQueue) {
FMDBDispatchQueueRelease(_lockQueue);
_lockQueue = 0x00;
}
#if ! __has_feature(objc_arc)
[super dealloc];
#endif
}
- (void)executeLocked:(void (^)(void))aBlock {
dispatch_sync(_lockQueue, aBlock);
}
- (void)pushDatabaseBackInPool:(FMDatabase*)db {
if (!db) { // db can be null if we set an upper bound on the # of databases to create.
return;
}
[self executeLocked:^() {
if ([_databaseInPool containsObject:db]) {
[[NSException exceptionWithName:@"Database already in pool" reason:@"The FMDatabase being put back into the pool is already present in the pool" userInfo:nil] raise];
}
[_databaseInPool addObject:db];
[_databaseOutPool removeObject:db];
}];
}
- (FMDatabase*)db {
__block FMDatabase *db;
[self executeLocked:^() {
db = [_databaseInPool lastObject];
if (db) {
[_databaseOutPool addObject:db];
[_databaseInPool removeLastObject];
}
else {
if (_maximumNumberOfDatabasesToCreate) {
NSUInteger currentCount = [_databaseOutPool count] + [_databaseInPool count];
if (currentCount >= _maximumNumberOfDatabasesToCreate) {
NSLog(@"Maximum number of databases (%ld) has already been reached!", (long)currentCount);
return;
}
}
db = [FMDatabase databaseWithPath:_path];
}
//This ensures that the db is opened before returning
if ([db open]) {
if ([_delegate respondsToSelector:@selector(databasePool:shouldAddDatabaseToPool:)] && ![_delegate databasePool:self shouldAddDatabaseToPool:db]) {
[db close];
db = 0x00;
}
else {
//It should not get added in the pool twice if lastObject was found
if (![_databaseOutPool containsObject:db]) {
[_databaseOutPool addObject:db];
}
}
}
else {
NSLog(@"Could not open up the database at path %@", _path);
db = 0x00;
}
}];
return db;
}
- (NSUInteger)countOfCheckedInDatabases {
__block NSUInteger count;
[self executeLocked:^() {
count = [_databaseInPool count];
}];
return count;
}
- (NSUInteger)countOfCheckedOutDatabases {
__block NSUInteger count;
[self executeLocked:^() {
count = [_databaseOutPool count];
}];
return count;
}
- (NSUInteger)countOfOpenDatabases {
__block NSUInteger count;
[self executeLocked:^() {
count = [_databaseOutPool count] + [_databaseInPool count];
}];
return count;
}
- (void)releaseAllDatabases {
[self executeLocked:^() {
[_databaseOutPool removeAllObjects];
[_databaseInPool removeAllObjects];
}];
}
- (void)inDatabase:(void (^)(FMDatabase *db))block {
FMDatabase *db = [self db];
block(db);
[self pushDatabaseBackInPool:db];
}
- (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block {
BOOL shouldRollback = NO;
FMDatabase *db = [self db];
if (useDeferred) {
[db beginDeferredTransaction];
}
else {
[db beginTransaction];
}
block(db, &shouldRollback);
if (shouldRollback) {
[db rollback];
}
else {
[db commit];
}
[self pushDatabaseBackInPool:db];
}
- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
[self beginTransaction:YES withBlock:block];
}
- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
[self beginTransaction:NO withBlock:block];
}
#if SQLITE_VERSION_NUMBER >= 3007000
- (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block {
static unsigned long savePointIdx = 0;
NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++];
BOOL shouldRollback = NO;
FMDatabase *db = [self db];
NSError *err = 0x00;
if (![db startSavePointWithName:name error:&err]) {
[self pushDatabaseBackInPool:db];
return err;
}
block(db, &shouldRollback);
if (shouldRollback) {
[db rollbackToSavePointWithName:name error:&err];
}
else {
[db releaseSavePointWithName:name error:&err];
}
[self pushDatabaseBackInPool:db];
return err;
}
#endif
@end

View file

@ -0,0 +1,38 @@
//
// FMDatabaseQueue.h
// fmdb
//
// Created by August Mueller on 6/22/11.
// Copyright 2011 Flying Meat Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "sqlite3.h"
@class FMDatabase;
@interface FMDatabaseQueue : NSObject {
NSString *_path;
dispatch_queue_t _queue;
FMDatabase *_db;
}
@property (atomic, retain) NSString *path;
+ (id)databaseQueueWithPath:(NSString*)aPath;
- (id)initWithPath:(NSString*)aPath;
- (void)close;
- (void)inDatabase:(void (^)(FMDatabase *db))block;
- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block;
- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block;
#if SQLITE_VERSION_NUMBER >= 3007000
// NOTE: you can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock.
// If you need to nest, use FMDatabase's startSavePointWithName:error: instead.
- (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block;
#endif
@end

View file

@ -0,0 +1,176 @@
//
// FMDatabaseQueue.m
// fmdb
//
// Created by August Mueller on 6/22/11.
// Copyright 2011 Flying Meat Inc. All rights reserved.
//
#import "FMDatabaseQueue.h"
#import "FMDatabase.h"
/*
Note: we call [self retain]; before using dispatch_sync, just incase
FMDatabaseQueue is released on another thread and we're in the middle of doing
something in dispatch_sync
*/
@implementation FMDatabaseQueue
@synthesize path = _path;
+ (id)databaseQueueWithPath:(NSString*)aPath {
FMDatabaseQueue *q = [[self alloc] initWithPath:aPath];
FMDBAutorelease(q);
return q;
}
- (id)initWithPath:(NSString*)aPath {
self = [super init];
if (self != nil) {
_db = [FMDatabase databaseWithPath:aPath];
FMDBRetain(_db);
if (![_db open]) {
NSLog(@"Could not create database queue for path %@", aPath);
FMDBRelease(self);
return 0x00;
}
_path = FMDBReturnRetained(aPath);
_queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);
}
return self;
}
- (void)dealloc {
FMDBRelease(_db);
FMDBRelease(_path);
if (_queue) {
FMDBDispatchQueueRelease(_queue);
_queue = 0x00;
}
#if ! __has_feature(objc_arc)
[super dealloc];
#endif
}
- (void)close {
FMDBRetain(self);
dispatch_sync(_queue, ^() {
[_db close];
FMDBRelease(_db);
_db = 0x00;
});
FMDBRelease(self);
}
- (FMDatabase*)database {
if (!_db) {
_db = FMDBReturnRetained([FMDatabase databaseWithPath:_path]);
if (![_db open]) {
NSLog(@"FMDatabaseQueue could not reopen database for path %@", _path);
FMDBRelease(_db);
_db = 0x00;
return 0x00;
}
}
return _db;
}
- (void)inDatabase:(void (^)(FMDatabase *db))block {
FMDBRetain(self);
dispatch_sync(_queue, ^() {
FMDatabase *db = [self database];
block(db);
if ([db hasOpenResultSets]) {
NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]");
}
});
FMDBRelease(self);
}
- (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block {
FMDBRetain(self);
dispatch_sync(_queue, ^() {
BOOL shouldRollback = NO;
if (useDeferred) {
[[self database] beginDeferredTransaction];
}
else {
[[self database] beginTransaction];
}
block([self database], &shouldRollback);
if (shouldRollback) {
[[self database] rollback];
}
else {
[[self database] commit];
}
});
FMDBRelease(self);
}
- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
[self beginTransaction:YES withBlock:block];
}
- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
[self beginTransaction:NO withBlock:block];
}
#if SQLITE_VERSION_NUMBER >= 3007000
- (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block {
static unsigned long savePointIdx = 0;
__block NSError *err = 0x00;
FMDBRetain(self);
dispatch_sync(_queue, ^() {
NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++];
BOOL shouldRollback = NO;
if ([[self database] startSavePointWithName:name error:&err]) {
block([self database], &shouldRollback);
if (shouldRollback) {
[[self database] rollbackToSavePointWithName:name error:&err];
}
else {
[[self database] releaseSavePointWithName:name error:&err];
}
}
});
FMDBRelease(self);
return err;
}
#endif
@end

View file

@ -0,0 +1,104 @@
#import <Foundation/Foundation.h>
#import "sqlite3.h"
#ifndef __has_feature // Optional.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
#ifndef NS_RETURNS_NOT_RETAINED
#if __has_feature(attribute_ns_returns_not_retained)
#define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained))
#else
#define NS_RETURNS_NOT_RETAINED
#endif
#endif
@class FMDatabase;
@class FMStatement;
@interface FMResultSet : NSObject {
FMDatabase *_parentDB;
FMStatement *_statement;
NSString *_query;
NSMutableDictionary *_columnNameToIndexMap;
}
@property (atomic, retain) NSString *query;
@property (readonly) NSMutableDictionary *columnNameToIndexMap;
@property (atomic, retain) FMStatement *statement;
+ (id)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB;
- (void)close;
- (void)setParentDB:(FMDatabase *)newDb;
- (BOOL)next;
- (BOOL)hasAnotherRow;
- (int)columnCount;
- (int)columnIndexForName:(NSString*)columnName;
- (NSString*)columnNameForIndex:(int)columnIdx;
- (int)intForColumn:(NSString*)columnName;
- (int)intForColumnIndex:(int)columnIdx;
- (long)longForColumn:(NSString*)columnName;
- (long)longForColumnIndex:(int)columnIdx;
- (long long int)longLongIntForColumn:(NSString*)columnName;
- (long long int)longLongIntForColumnIndex:(int)columnIdx;
- (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName;
- (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx;
- (BOOL)boolForColumn:(NSString*)columnName;
- (BOOL)boolForColumnIndex:(int)columnIdx;
- (double)doubleForColumn:(NSString*)columnName;
- (double)doubleForColumnIndex:(int)columnIdx;
- (NSString*)stringForColumn:(NSString*)columnName;
- (NSString*)stringForColumnIndex:(int)columnIdx;
- (NSDate*)dateForColumn:(NSString*)columnName;
- (NSDate*)dateForColumnIndex:(int)columnIdx;
- (NSData*)dataForColumn:(NSString*)columnName;
- (NSData*)dataForColumnIndex:(int)columnIdx;
- (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx;
- (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName;
// returns one of NSNumber, NSString, NSData, or NSNull
- (id)objectForColumnName:(NSString*)columnName;
- (id)objectForColumnIndex:(int)columnIdx;
- (id)objectForKeyedSubscript:(NSString *)columnName;
- (id)objectAtIndexedSubscript:(int)columnIdx;
/*
If you are going to use this data after you iterate over the next row, or after you close the
result set, make sure to make a copy of the data first (or just use dataForColumn:/dataForColumnIndex:)
If you don't, you're going to be in a world of hurt when you try and use the data.
*/
- (NSData*)dataNoCopyForColumn:(NSString*)columnName NS_RETURNS_NOT_RETAINED;
- (NSData*)dataNoCopyForColumnIndex:(int)columnIdx NS_RETURNS_NOT_RETAINED;
- (BOOL)columnIndexIsNull:(int)columnIdx;
- (BOOL)columnIsNull:(NSString*)columnName;
/* Returns a dictionary of the row results mapped to case sensitive keys of the column names. */
- (NSDictionary*)resultDictionary;
/* Please use resultDictionary instead. Also, beware that resultDictionary is case sensitive! */
- (NSDictionary*)resultDict __attribute__ ((deprecated));
- (void)kvcMagic:(id)object;
@end

View file

@ -0,0 +1,413 @@
#import "FMResultSet.h"
#import "FMDatabase.h"
#import "unistd.h"
@interface FMDatabase ()
- (void)resultSetDidClose:(FMResultSet *)resultSet;
@end
@implementation FMResultSet
@synthesize query=_query;
@synthesize statement=_statement;
+ (id)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB {
FMResultSet *rs = [[FMResultSet alloc] init];
[rs setStatement:statement];
[rs setParentDB:aDB];
return FMDBReturnAutoreleased(rs);
}
- (void)finalize {
[self close];
[super finalize];
}
- (void)dealloc {
[self close];
FMDBRelease(_query);
_query = nil;
FMDBRelease(_columnNameToIndexMap);
_columnNameToIndexMap = nil;
#if ! __has_feature(objc_arc)
[super dealloc];
#endif
}
- (void)close {
[_statement reset];
FMDBRelease(_statement);
_statement = nil;
// we don't need this anymore... (i think)
//[_parentDB setInUse:NO];
[_parentDB resultSetDidClose:self];
_parentDB = nil;
}
- (int)columnCount {
return sqlite3_column_count([_statement statement]);
}
- (NSMutableDictionary *)columnNameToIndexMap {
if (!_columnNameToIndexMap) {
int columnCount = sqlite3_column_count([_statement statement]);
_columnNameToIndexMap = [[NSMutableDictionary alloc] initWithCapacity:(NSUInteger)columnCount];
int columnIdx = 0;
for (columnIdx = 0; columnIdx < columnCount; columnIdx++) {
[_columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx]
forKey:[[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)] lowercaseString]];
}
}
return _columnNameToIndexMap;
}
- (void)kvcMagic:(id)object {
int columnCount = sqlite3_column_count([_statement statement]);
int columnIdx = 0;
for (columnIdx = 0; columnIdx < columnCount; columnIdx++) {
const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx);
// check for a null row
if (c) {
NSString *s = [NSString stringWithUTF8String:c];
[object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]];
}
}
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
- (NSDictionary*)resultDict {
NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]);
if (num_cols > 0) {
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols];
NSEnumerator *columnNames = [[self columnNameToIndexMap] keyEnumerator];
NSString *columnName = nil;
while ((columnName = [columnNames nextObject])) {
id objectValue = [self objectForColumnName:columnName];
[dict setObject:objectValue forKey:columnName];
}
return FMDBReturnAutoreleased([dict copy]);
}
else {
NSLog(@"Warning: There seem to be no columns in this set.");
}
return nil;
}
#pragma clang diagnostic pop
- (NSDictionary*)resultDictionary {
NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]);
if (num_cols > 0) {
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols];
int columnCount = sqlite3_column_count([_statement statement]);
int columnIdx = 0;
for (columnIdx = 0; columnIdx < columnCount; columnIdx++) {
NSString *columnName = [NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)];
id objectValue = [self objectForColumnIndex:columnIdx];
[dict setObject:objectValue forKey:columnName];
}
return dict;
}
else {
NSLog(@"Warning: There seem to be no columns in this set.");
}
return nil;
}
- (BOOL)next {
int rc;
BOOL retry;
int numberOfRetries = 0;
do {
retry = NO;
rc = sqlite3_step([_statement statement]);
if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
// this will happen if the db is locked, like if we are doing an update or insert.
// in that case, retry the step... and maybe wait just 10 milliseconds.
retry = YES;
if (SQLITE_LOCKED == rc) {
rc = sqlite3_reset([_statement statement]);
if (rc != SQLITE_LOCKED) {
NSLog(@"Unexpected result from sqlite3_reset (%d) rs", rc);
}
}
usleep(20);
if ([_parentDB busyRetryTimeout] && (numberOfRetries++ > [_parentDB busyRetryTimeout])) {
NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [_parentDB databasePath]);
NSLog(@"Database busy");
break;
}
}
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]));
break;
}
else if (SQLITE_MISUSE == rc) {
// uh oh.
NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle]));
break;
}
else {
// wtf?
NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle]));
break;
}
} while (retry);
if (rc != SQLITE_ROW) {
[self close];
}
return (rc == SQLITE_ROW);
}
- (BOOL)hasAnotherRow {
return sqlite3_errcode([_parentDB sqliteHandle]) == SQLITE_ROW;
}
- (int)columnIndexForName:(NSString*)columnName {
columnName = [columnName lowercaseString];
NSNumber *n = [[self columnNameToIndexMap] objectForKey:columnName];
if (n) {
return [n intValue];
}
NSLog(@"Warning: I could not find the column named '%@'.", columnName);
return -1;
}
- (int)intForColumn:(NSString*)columnName {
return [self intForColumnIndex:[self columnIndexForName:columnName]];
}
- (int)intForColumnIndex:(int)columnIdx {
return sqlite3_column_int([_statement statement], columnIdx);
}
- (long)longForColumn:(NSString*)columnName {
return [self longForColumnIndex:[self columnIndexForName:columnName]];
}
- (long)longForColumnIndex:(int)columnIdx {
return (long)sqlite3_column_int64([_statement statement], columnIdx);
}
- (long long int)longLongIntForColumn:(NSString*)columnName {
return [self longLongIntForColumnIndex:[self columnIndexForName:columnName]];
}
- (long long int)longLongIntForColumnIndex:(int)columnIdx {
return sqlite3_column_int64([_statement statement], columnIdx);
}
- (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName {
return [self unsignedLongLongIntForColumnIndex:[self columnIndexForName:columnName]];
}
- (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx {
return (unsigned long long int)[self longLongIntForColumnIndex:columnIdx];
}
- (BOOL)boolForColumn:(NSString*)columnName {
return [self boolForColumnIndex:[self columnIndexForName:columnName]];
}
- (BOOL)boolForColumnIndex:(int)columnIdx {
return ([self intForColumnIndex:columnIdx] != 0);
}
- (double)doubleForColumn:(NSString*)columnName {
return [self doubleForColumnIndex:[self columnIndexForName:columnName]];
}
- (double)doubleForColumnIndex:(int)columnIdx {
return sqlite3_column_double([_statement statement], columnIdx);
}
- (NSString*)stringForColumnIndex:(int)columnIdx {
if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
return nil;
}
const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx);
if (!c) {
// null row.
return nil;
}
return [NSString stringWithUTF8String:c];
}
- (NSString*)stringForColumn:(NSString*)columnName {
return [self stringForColumnIndex:[self columnIndexForName:columnName]];
}
- (NSDate*)dateForColumn:(NSString*)columnName {
return [self dateForColumnIndex:[self columnIndexForName:columnName]];
}
- (NSDate*)dateForColumnIndex:(int)columnIdx {
if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
return nil;
}
return [_parentDB hasDateFormatter] ? [_parentDB dateFromString:[self stringForColumnIndex:columnIdx]] : [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]];
}
- (NSData*)dataForColumn:(NSString*)columnName {
return [self dataForColumnIndex:[self columnIndexForName:columnName]];
}
- (NSData*)dataForColumnIndex:(int)columnIdx {
if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
return nil;
}
int dataSize = sqlite3_column_bytes([_statement statement], columnIdx);
NSMutableData *data = [NSMutableData dataWithLength:(NSUInteger)dataSize];
memcpy([data mutableBytes], sqlite3_column_blob([_statement statement], columnIdx), dataSize);
return data;
}
- (NSData*)dataNoCopyForColumn:(NSString*)columnName {
return [self dataNoCopyForColumnIndex:[self columnIndexForName:columnName]];
}
- (NSData*)dataNoCopyForColumnIndex:(int)columnIdx {
if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
return nil;
}
int dataSize = sqlite3_column_bytes([_statement statement], columnIdx);
NSData *data = [NSData dataWithBytesNoCopy:(void *)sqlite3_column_blob([_statement statement], columnIdx) length:(NSUInteger)dataSize freeWhenDone:NO];
return data;
}
- (BOOL)columnIndexIsNull:(int)columnIdx {
return sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL;
}
- (BOOL)columnIsNull:(NSString*)columnName {
return [self columnIndexIsNull:[self columnIndexForName:columnName]];
}
- (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx {
if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
return nil;
}
return sqlite3_column_text([_statement statement], columnIdx);
}
- (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName {
return [self UTF8StringForColumnIndex:[self columnIndexForName:columnName]];
}
- (id)objectForColumnIndex:(int)columnIdx {
int columnType = sqlite3_column_type([_statement statement], columnIdx);
id returnValue = nil;
if (columnType == SQLITE_INTEGER) {
returnValue = [NSNumber numberWithLongLong:[self longLongIntForColumnIndex:columnIdx]];
}
else if (columnType == SQLITE_FLOAT) {
returnValue = [NSNumber numberWithDouble:[self doubleForColumnIndex:columnIdx]];
}
else if (columnType == SQLITE_BLOB) {
returnValue = [self dataForColumnIndex:columnIdx];
}
else {
//default to a string for everything else
returnValue = [self stringForColumnIndex:columnIdx];
}
if (returnValue == nil) {
returnValue = [NSNull null];
}
return returnValue;
}
- (id)objectForColumnName:(NSString*)columnName {
return [self objectForColumnIndex:[self columnIndexForName:columnName]];
}
// returns autoreleased NSString containing the name of the column in the result set
- (NSString*)columnNameForIndex:(int)columnIdx {
return [NSString stringWithUTF8String: sqlite3_column_name([_statement statement], columnIdx)];
}
- (void)setParentDB:(FMDatabase *)newDb {
_parentDB = newDb;
}
- (id)objectAtIndexedSubscript:(int)columnIdx {
return [self objectForColumnIndex:columnIdx];
}
- (id)objectForKeyedSubscript:(NSString *)columnName {
return [self objectForColumnName:columnName];
}
@end

File diff suppressed because it is too large Load diff