3 @implementation FMDatabase
5 + (id)databaseWithPath:(NSString*)aPath {
6 return [[[FMDatabase alloc] initWithPath:aPath] autorelease];
9 - (id)initWithPath:(NSString*)aPath {
13 databasePath = [aPath copy];
17 busyRetryTimeout = 0x00;
25 [databasePath release];
29 + (NSString*) sqliteLibVersion {
30 return [NSString stringWithFormat:@"%s", sqlite3_libversion()];
33 - (NSString *) databasePath {
37 - (sqlite3*) sqliteHandle {
42 int err = sqlite3_open( [databasePath fileSystemRepresentation], &db );
43 if(err != SQLITE_OK) {
44 NSLog(@"error opening!: %d", err);
58 int numberOfRetries = 0;
61 rc = sqlite3_close(db);
62 if (SQLITE_BUSY == rc) {
65 if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
66 NSLog(@"%s:%d", __FUNCTION__, __LINE__);
67 NSLog(@"Database busy, unable to close");
71 else if (SQLITE_OK != rc) {
72 NSLog(@"error closing!: %d", rc);
80 #ifdef SQLITE_HAS_CODEC
82 - (BOOL) rekey:(NSString*)key {
88 int rc = sqlite3_rekey(db, [key UTF8String], strlen([key UTF8String]));
90 if (rc != SQLITE_OK) {
91 NSLog(@"error on rekey: %d", rc);
92 NSLog(@"%@", [self lastErrorMessage]);
95 return (rc == SQLITE_OK);
98 - (BOOL) setKey:(NSString*)key {
103 int rc = sqlite3_key(db, [key UTF8String], strlen([key UTF8String]));
105 return (rc == SQLITE_OK);
110 - (BOOL) goodConnection {
116 FMResultSet *rs = [self executeQuery:@"select name from sqlite_master where type='table'"];
126 - (void) compainAboutInUse {
127 NSLog(@"The FMDatabase %@ is currently in use.", self);
130 *(long*)0 = 0xDEADBEEF;
134 - (NSString*) lastErrorMessage {
135 return [NSString stringWithUTF8String:sqlite3_errmsg(db)];
139 return ([self lastErrorCode] != SQLITE_OK);
142 - (int) lastErrorCode {
143 return sqlite3_errcode(db);
146 - (sqlite_int64) lastInsertRowId {
149 [self compainAboutInUse];
154 sqlite_int64 ret = sqlite3_last_insert_rowid(db);
161 - (void) bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt; {
163 // FIXME - someday check the return codes on these binds.
164 if ([obj isKindOfClass:[NSData class]]) {
165 sqlite3_bind_blob(pStmt, idx, [obj bytes], [obj length], SQLITE_STATIC);
167 else if ([obj isKindOfClass:[NSDate class]]) {
168 sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]);
170 else if ([obj isKindOfClass:[NSNumber class]]) {
172 if (strcmp([obj objCType], @encode(BOOL)) == 0) {
173 sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0));
175 else if (strcmp([obj objCType], @encode(int)) == 0) {
176 sqlite3_bind_int64(pStmt, idx, [obj longValue]);
178 else if (strcmp([obj objCType], @encode(float)) == 0) {
179 sqlite3_bind_double(pStmt, idx, [obj floatValue]);
181 else if (strcmp([obj objCType], @encode(double)) == 0) {
182 sqlite3_bind_double(pStmt, idx, [obj doubleValue]);
185 sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
189 sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
193 - (id) executeQuery:(NSString*)objs, ... {
196 [self compainAboutInUse];
202 FMResultSet *rs = nil;
204 NSString *sql = objs;
208 if (traceExecution && sql) {
209 NSLog(@"%@ executeQuery: %@", self, sql);
212 NSLog(@"sql: %@", sql);
214 int numberOfRetries = 0;
218 rc = sqlite3_prepare(db, [sql UTF8String], -1, &pStmt, 0);
220 if (SQLITE_BUSY == rc) {
224 if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
225 NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
226 NSLog(@"Database busy");
230 else if (SQLITE_OK != rc) {
232 rc = sqlite3_finalize(pStmt);
235 NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
236 NSLog(@"DB Query: %@", sql);
238 #ifdef __BIG_ENDIAN__
241 *(long*)0 = 0xDEADBEEF;
253 int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!)
255 va_start(argList, objs);
257 while (idx < queryCount) {
258 obj = va_arg(argList, id);
264 NSLog(@"obj: %@", obj);
268 [self bindObject:obj toColumn:idx inStatement:pStmt];
273 if (idx != queryCount) {
274 NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)");
275 sqlite3_finalize(pStmt);
280 // the statement gets close in rs's dealloc or [rs close];
281 rs = [FMResultSet resultSetWithStatement:pStmt usingParentDatabase:self];
288 - (BOOL) executeUpdate:(NSString*)objs, ... {
291 [self compainAboutInUse];
296 NSString *sql = objs;
298 sqlite3_stmt *pStmt = 0x00;
300 if (traceExecution && sql) {
301 NSLog(@"%@ executeUpdate: %@", self, sql);
304 int numberOfRetries = 0;
308 rc = sqlite3_prepare(db, [sql UTF8String], -1, &pStmt, 0);
309 if (SQLITE_BUSY == rc) {
312 if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
313 NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
314 NSLog(@"Database busy");
318 else if (SQLITE_OK != rc) {
320 rc = sqlite3_finalize(pStmt);
323 NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
324 NSLog(@"DB Query: %@", sql);
326 #ifdef __BIG_ENDIAN__
329 *(long*)0 = 0xDEADBEEF;
342 int queryCount = sqlite3_bind_parameter_count(pStmt);
344 va_start(argList, objs);
346 while (idx < queryCount) {
348 obj = va_arg(argList, id);
356 [self bindObject:obj toColumn:idx inStatement:pStmt];
362 if (idx != queryCount) {
363 NSLog(@"Error: the bind count is not correct for the # of variables (%@) (executeUpdate)", sql);
364 sqlite3_finalize(pStmt);
369 /* Call sqlite3_step() to run the virtual machine. Since the SQL being
370 ** executed is not a SELECT statement, we assume no data will be returned.
374 rc = sqlite3_step(pStmt);
377 if (SQLITE_BUSY == rc) {
378 // this will happen if the db is locked, like if we are doing an update or insert.
379 // in that case, retry the step... and maybe wait just 10 milliseconds.
382 if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
383 NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
384 NSLog(@"Database busy");
388 else if (SQLITE_DONE == rc || SQLITE_ROW == rc) {
389 // all is well, let's return.
391 else if (SQLITE_ERROR == rc) {
392 NSLog(@"Error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(db));
393 NSLog(@"DB Query: %@", sql);
395 else if (SQLITE_MISUSE == rc) {
397 NSLog(@"Error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(db));
398 NSLog(@"DB Query: %@", sql);
402 NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(db));
403 NSLog(@"DB Query: %@", sql);
408 assert( rc!=SQLITE_ROW );
410 /* Finalize the virtual machine. This releases all memory and other
411 ** resources allocated by the sqlite3_prepare() call above.
413 rc = sqlite3_finalize(pStmt);
417 return (rc == SQLITE_OK);
421 BOOL b = [self executeUpdate:@"ROLLBACK TRANSACTION;"];
429 BOOL b = [self executeUpdate:@"COMMIT TRANSACTION;"];
436 - (BOOL) beginDeferredTransaction {
437 BOOL b = [self executeUpdate:@"BEGIN DEFERRED TRANSACTION;"];
444 - (BOOL) beginTransaction {
445 BOOL b = [self executeUpdate:@"BEGIN EXCLUSIVE TRANSACTION;"];
455 - (void)setLogsErrors:(BOOL)flag {
459 - (BOOL)crashOnErrors {
460 return crashOnErrors;
462 - (void)setCrashOnErrors:(BOOL)flag {
463 crashOnErrors = flag;
467 return inUse || inTransaction;
469 - (void)setInUse:(BOOL)flag {
474 - (BOOL)inTransaction {
475 return inTransaction;
477 - (void)setInTransaction:(BOOL)flag {
478 inTransaction = flag;
481 - (BOOL)traceExecution {
482 return traceExecution;
484 - (void)setTraceExecution:(BOOL)flag {
485 traceExecution = flag;
491 - (void)setCheckedOut:(BOOL)flag {
496 - (int)busyRetryTimeout {
497 return busyRetryTimeout;
499 - (void)setBusyRetryTimeout:(int)newBusyRetryTimeout {
500 busyRetryTimeout = newBusyRetryTimeout;