|
@@ -5,7 +5,6 @@
|
|
|
|
|
|
|
|
static FMDBExecuteBulkSQLCallbackBlock execCallbackBlock;
|
|
static FMDBExecuteBulkSQLCallbackBlock execCallbackBlock;
|
|
|
|
|
|
|
|
-
|
|
|
|
|
@interface FMDatabase ()
|
|
@interface FMDatabase ()
|
|
|
|
|
|
|
|
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args;
|
|
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args;
|
|
@@ -20,19 +19,12 @@ @implementation FMDatabase
|
|
|
@synthesize checkedOut=_checkedOut;
|
|
@synthesize checkedOut=_checkedOut;
|
|
|
@synthesize traceExecution=_traceExecution;
|
|
@synthesize traceExecution=_traceExecution;
|
|
|
|
|
|
|
|
|
|
+#pragma mark FMDatabase instantiation and deallocation
|
|
|
|
|
+
|
|
|
+ (instancetype)databaseWithPath:(NSString*)aPath {
|
|
+ (instancetype)databaseWithPath:(NSString*)aPath {
|
|
|
return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]);
|
|
return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-+ (NSString*)sqliteLibVersion {
|
|
|
|
|
- return [NSString stringWithFormat:@"%s", sqlite3_libversion()];
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-+ (BOOL)isSQLiteThreadSafe {
|
|
|
|
|
- // make sure to read the sqlite headers on this guy!
|
|
|
|
|
- return sqlite3_threadsafe() != 0;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
- (instancetype)init {
|
|
- (instancetype)init {
|
|
|
return [self initWithPath:nil];
|
|
return [self initWithPath:nil];
|
|
|
}
|
|
}
|
|
@@ -78,6 +70,17 @@ - (NSString *)databasePath {
|
|
|
return _databasePath;
|
|
return _databasePath;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#pragma mark SQLite information
|
|
|
|
|
+
|
|
|
|
|
++ (NSString*)sqliteLibVersion {
|
|
|
|
|
+ return [NSString stringWithFormat:@"%s", sqlite3_libversion()];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
++ (BOOL)isSQLiteThreadSafe {
|
|
|
|
|
+ // make sure to read the sqlite headers on this guy!
|
|
|
|
|
+ return sqlite3_threadsafe() != 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
- (sqlite3*)sqliteHandle {
|
|
- (sqlite3*)sqliteHandle {
|
|
|
return _db;
|
|
return _db;
|
|
|
}
|
|
}
|
|
@@ -96,6 +99,8 @@ - (const char*)sqlitePath {
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#pragma mark Open and close database
|
|
|
|
|
+
|
|
|
- (BOOL)open {
|
|
- (BOOL)open {
|
|
|
if (_db) {
|
|
if (_db) {
|
|
|
return YES;
|
|
return YES;
|
|
@@ -175,9 +180,15 @@ - (BOOL)close {
|
|
|
return YES;
|
|
return YES;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#pragma mark Busy handler routines
|
|
|
|
|
|
|
|
-static int FMDatabaseBusyHandler(void *f, int count) {
|
|
|
|
|
-
|
|
|
|
|
|
|
+// NOTE: appledoc seems to choke on this function for some reason;
|
|
|
|
|
+// so when generating documentation, you might want to temporarily
|
|
|
|
|
+// comment out this function. It's a known bug that it has problems
|
|
|
|
|
+// with C functions within a class implementation, but for some
|
|
|
|
|
+// reason, only this C function causes problems; the rest don't!
|
|
|
|
|
+
|
|
|
|
|
+static int FMDBDatabaseBusyHandler(void *f, int count) {
|
|
|
FMDatabase *self = (__bridge FMDatabase*)f;
|
|
FMDatabase *self = (__bridge FMDatabase*)f;
|
|
|
|
|
|
|
|
if (count == 0) {
|
|
if (count == 0) {
|
|
@@ -204,7 +215,7 @@ - (void)setMaxBusyRetryTimeInterval:(NSTimeInterval)timeout {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (timeout > 0) {
|
|
if (timeout > 0) {
|
|
|
- sqlite3_busy_handler(_db, &FMDatabaseBusyHandler, (__bridge void *)(self));
|
|
|
|
|
|
|
+ sqlite3_busy_handler(_db, &FMDBDatabaseBusyHandler, (__bridge void *)(self));
|
|
|
}
|
|
}
|
|
|
else {
|
|
else {
|
|
|
// turn it off otherwise
|
|
// turn it off otherwise
|
|
@@ -231,17 +242,7 @@ - (void)setBusyRetryTimeout:(int)i {
|
|
|
NSLog(@"FMDB: setBusyRetryTimeout does nothing, please use setRetryTimeout:");
|
|
NSLog(@"FMDB: setBusyRetryTimeout does nothing, please use setRetryTimeout:");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-- (void)clearCachedStatements {
|
|
|
|
|
-
|
|
|
|
|
- for (NSMutableSet *statements in [_cachedStatements objectEnumerator]) {
|
|
|
|
|
- [statements makeObjectsPerformSelector:@selector(close)];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- [_cachedStatements removeAllObjects];
|
|
|
|
|
-}
|
|
|
|
|
|
|
+#pragma mark Result set functions
|
|
|
|
|
|
|
|
- (BOOL)hasOpenResultSets {
|
|
- (BOOL)hasOpenResultSets {
|
|
|
return [_openResultSets count] > 0;
|
|
return [_openResultSets count] > 0;
|
|
@@ -267,6 +268,17 @@ - (void)resultSetDidClose:(FMResultSet *)resultSet {
|
|
|
[_openResultSets removeObject:setValue];
|
|
[_openResultSets removeObject:setValue];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#pragma mark Cached statements
|
|
|
|
|
+
|
|
|
|
|
+- (void)clearCachedStatements {
|
|
|
|
|
+
|
|
|
|
|
+ for (NSMutableSet *statements in [_cachedStatements objectEnumerator]) {
|
|
|
|
|
+ [statements makeObjectsPerformSelector:@selector(close)];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [_cachedStatements removeAllObjects];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
- (FMStatement*)cachedStatementForQuery:(NSString*)query {
|
|
- (FMStatement*)cachedStatementForQuery:(NSString*)query {
|
|
|
|
|
|
|
|
NSMutableSet* statements = [_cachedStatements objectForKey:query];
|
|
NSMutableSet* statements = [_cachedStatements objectForKey:query];
|
|
@@ -297,6 +309,8 @@ - (void)setCachedStatement:(FMStatement*)statement forQuery:(NSString*)query {
|
|
|
FMDBRelease(query);
|
|
FMDBRelease(query);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#pragma mark Key routines
|
|
|
|
|
+
|
|
|
- (BOOL)rekey:(NSString*)key {
|
|
- (BOOL)rekey:(NSString*)key {
|
|
|
NSData *keyData = [NSData dataWithBytes:(void *)[key UTF8String] length:(NSUInteger)strlen([key UTF8String])];
|
|
NSData *keyData = [NSData dataWithBytes:(void *)[key UTF8String] length:(NSUInteger)strlen([key UTF8String])];
|
|
|
|
|
|
|
@@ -342,6 +356,8 @@ - (BOOL)setKeyWithData:(NSData *)keyData {
|
|
|
#endif
|
|
#endif
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#pragma mark Date routines
|
|
|
|
|
+
|
|
|
+ (NSDateFormatter *)storeableDateFormat:(NSString *)format {
|
|
+ (NSDateFormatter *)storeableDateFormat:(NSString *)format {
|
|
|
|
|
|
|
|
NSDateFormatter *result = FMDBReturnAutoreleased([[NSDateFormatter alloc] init]);
|
|
NSDateFormatter *result = FMDBReturnAutoreleased([[NSDateFormatter alloc] init]);
|
|
@@ -369,6 +385,7 @@ - (NSString *)stringFromDate:(NSDate *)date {
|
|
|
return [_dateFormat stringFromDate:date];
|
|
return [_dateFormat stringFromDate:date];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#pragma mark State of database
|
|
|
|
|
|
|
|
- (BOOL)goodConnection {
|
|
- (BOOL)goodConnection {
|
|
|
|
|
|
|
@@ -416,6 +433,8 @@ - (BOOL)databaseExists {
|
|
|
return YES;
|
|
return YES;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#pragma mark Error routines
|
|
|
|
|
+
|
|
|
- (NSString*)lastErrorMessage {
|
|
- (NSString*)lastErrorMessage {
|
|
|
return [NSString stringWithUTF8String:sqlite3_errmsg(_db)];
|
|
return [NSString stringWithUTF8String:sqlite3_errmsg(_db)];
|
|
|
}
|
|
}
|
|
@@ -430,7 +449,6 @@ - (int)lastErrorCode {
|
|
|
return sqlite3_errcode(_db);
|
|
return sqlite3_errcode(_db);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-
|
|
|
|
|
- (NSError*)errorWithMessage:(NSString*)message {
|
|
- (NSError*)errorWithMessage:(NSString*)message {
|
|
|
NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey];
|
|
NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey];
|
|
|
|
|
|
|
@@ -441,6 +459,8 @@ - (NSError*)lastError {
|
|
|
return [self errorWithMessage:[self lastErrorMessage]];
|
|
return [self errorWithMessage:[self lastErrorMessage]];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#pragma mark Update information routines
|
|
|
|
|
+
|
|
|
- (sqlite_int64)lastInsertRowId {
|
|
- (sqlite_int64)lastInsertRowId {
|
|
|
|
|
|
|
|
if (_isExecutingStatement) {
|
|
if (_isExecutingStatement) {
|
|
@@ -472,6 +492,8 @@ - (int)changes {
|
|
|
return ret;
|
|
return ret;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#pragma mark SQL manipulation
|
|
|
|
|
+
|
|
|
- (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt {
|
|
- (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt {
|
|
|
|
|
|
|
|
if ((!obj) || ((NSNull *)obj == [NSNull null])) {
|
|
if ((!obj) || ((NSNull *)obj == [NSNull null])) {
|
|
@@ -664,6 +686,8 @@ - (void)extractSQL:(NSString *)sql argumentsList:(va_list)args intoString:(NSMut
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#pragma mark Execute queries
|
|
|
|
|
+
|
|
|
- (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments {
|
|
- (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments {
|
|
|
return [self executeQuery:sql withArgumentsInArray:nil orDictionary:arguments orVAList:nil];
|
|
return [self executeQuery:sql withArgumentsInArray:nil orDictionary:arguments orVAList:nil];
|
|
|
}
|
|
}
|
|
@@ -841,6 +865,8 @@ - (FMResultSet *)executeQuery:(NSString*)sql withVAList:(va_list)args {
|
|
|
return [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args];
|
|
return [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#pragma mark Execute updates
|
|
|
|
|
+
|
|
|
- (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr 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 {
|
|
|
|
|
|
|
|
if (![self databaseExists]) {
|
|
if (![self databaseExists]) {
|
|
@@ -1066,7 +1092,7 @@ - (BOOL)executeUpdateWithFormat:(NSString*)format, ... {
|
|
|
return [self executeUpdate:sql withArgumentsInArray:arguments];
|
|
return [self executeUpdate:sql withArgumentsInArray:arguments];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-int executeBulkSQLCallback(void *userInfo, int columns, char **values, char**names)
|
|
|
|
|
|
|
+int FMDBExecuteBulkSQLCallback(void *userInfo, int columns, char **values, char**names)
|
|
|
{
|
|
{
|
|
|
if (!execCallbackBlock) {
|
|
if (!execCallbackBlock) {
|
|
|
return 0;
|
|
return 0;
|
|
@@ -1109,7 +1135,7 @@ - (BOOL)executeBulkSQL:(NSString *)sql block:(FMDBExecuteBulkSQLCallbackBlock)bl
|
|
|
execCallbackBlock = block;
|
|
execCallbackBlock = block;
|
|
|
|
|
|
|
|
if (execCallbackBlock) {
|
|
if (execCallbackBlock) {
|
|
|
- rc = sqlite3_exec(self.sqliteHandle, [sql UTF8String], executeBulkSQLCallback, NULL, NULL);
|
|
|
|
|
|
|
+ rc = sqlite3_exec(self.sqliteHandle, [sql UTF8String], FMDBExecuteBulkSQLCallback, NULL, NULL);
|
|
|
} else {
|
|
} else {
|
|
|
rc = sqlite3_exec(self.sqliteHandle, [sql UTF8String], NULL, NULL, NULL);
|
|
rc = sqlite3_exec(self.sqliteHandle, [sql UTF8String], NULL, NULL, NULL);
|
|
|
}
|
|
}
|
|
@@ -1129,6 +1155,8 @@ - (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ... {
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#pragma mark Transactions
|
|
|
|
|
+
|
|
|
- (BOOL)rollback {
|
|
- (BOOL)rollback {
|
|
|
BOOL b = [self executeUpdate:@"rollback transaction"];
|
|
BOOL b = [self executeUpdate:@"rollback transaction"];
|
|
|
|
|
|
|
@@ -1175,7 +1203,7 @@ - (BOOL)inTransaction {
|
|
|
|
|
|
|
|
#if SQLITE_VERSION_NUMBER >= 3007000
|
|
#if SQLITE_VERSION_NUMBER >= 3007000
|
|
|
|
|
|
|
|
-static NSString *FMEscapeSavePointName(NSString *savepointName) {
|
|
|
|
|
|
|
+static NSString *FMDBEscapeSavePointName(NSString *savepointName) {
|
|
|
return [savepointName stringByReplacingOccurrencesOfString:@"'" withString:@"''"];
|
|
return [savepointName stringByReplacingOccurrencesOfString:@"'" withString:@"''"];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1183,7 +1211,7 @@ - (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr {
|
|
|
|
|
|
|
|
NSParameterAssert(name);
|
|
NSParameterAssert(name);
|
|
|
|
|
|
|
|
- NSString *sql = [NSString stringWithFormat:@"savepoint '%@';", FMEscapeSavePointName(name)];
|
|
|
|
|
|
|
+ NSString *sql = [NSString stringWithFormat:@"savepoint '%@';", FMDBEscapeSavePointName(name)];
|
|
|
|
|
|
|
|
if (![self executeUpdate:sql]) {
|
|
if (![self executeUpdate:sql]) {
|
|
|
|
|
|
|
@@ -1201,7 +1229,7 @@ - (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr {
|
|
|
|
|
|
|
|
NSParameterAssert(name);
|
|
NSParameterAssert(name);
|
|
|
|
|
|
|
|
- NSString *sql = [NSString stringWithFormat:@"release savepoint '%@';", FMEscapeSavePointName(name)];
|
|
|
|
|
|
|
+ NSString *sql = [NSString stringWithFormat:@"release savepoint '%@';", FMDBEscapeSavePointName(name)];
|
|
|
BOOL worked = [self executeUpdate:sql];
|
|
BOOL worked = [self executeUpdate:sql];
|
|
|
|
|
|
|
|
if (!worked && outErr) {
|
|
if (!worked && outErr) {
|
|
@@ -1215,7 +1243,7 @@ - (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr {
|
|
|
|
|
|
|
|
NSParameterAssert(name);
|
|
NSParameterAssert(name);
|
|
|
|
|
|
|
|
- NSString *sql = [NSString stringWithFormat:@"rollback transaction to savepoint '%@';", FMEscapeSavePointName(name)];
|
|
|
|
|
|
|
+ NSString *sql = [NSString stringWithFormat:@"rollback transaction to savepoint '%@';", FMDBEscapeSavePointName(name)];
|
|
|
BOOL worked = [self executeUpdate:sql];
|
|
BOOL worked = [self executeUpdate:sql];
|
|
|
|
|
|
|
|
if (!worked && outErr) {
|
|
if (!worked && outErr) {
|
|
@@ -1251,6 +1279,7 @@ - (NSError*)inSavePoint:(void (^)(BOOL *rollback))block {
|
|
|
|
|
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
|
|
+#pragma mark Cache statements
|
|
|
|
|
|
|
|
- (BOOL)shouldCacheStatements {
|
|
- (BOOL)shouldCacheStatements {
|
|
|
return _shouldCacheStatements;
|
|
return _shouldCacheStatements;
|
|
@@ -1269,7 +1298,8 @@ - (void)setShouldCacheStatements:(BOOL)value {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv);
|
|
|
|
|
|
|
+#pragma mark Callback function
|
|
|
|
|
+
|
|
|
void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
|
void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
|
|
#if ! __has_feature(objc_arc)
|
|
#if ! __has_feature(objc_arc)
|
|
|
void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (id)sqlite3_user_data(context);
|
|
void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (id)sqlite3_user_data(context);
|