Added dataNoCopyForColumn: and dataNoCopyForColumnIndex: to FMResultSet.
[fmdb.git] / src / FMDatabase.m
blob4595fa65f2bcd8d20ee3e68dad962d8d7b54ccc7
1 #import "FMDatabase.h"
3 @implementation FMDatabase
5 + (id)databaseWithPath:(NSString*)aPath {
6     return [[[FMDatabase alloc] initWithPath:aPath] autorelease];
9 - (id)initWithPath:(NSString*)aPath {
10     self = [super init];
11         
12     if (self) {
13         databasePath        = [aPath copy];
14         db                  = 0x00;
15         logsErrors          = 0x00;
16         crashOnErrors       = 0x00;
17         busyRetryTimeout    = 0x00;
18     }
19         
20         return self;
23 - (void)dealloc {
24         [self close];
25     
26     [cachedStatements release];
27     [databasePath release];
28         
29     [super dealloc];
32 + (NSString*) sqliteLibVersion {
33     return [NSString stringWithFormat:@"%s", sqlite3_libversion()];
36 - (NSString *) databasePath {
37     return databasePath;
40 - (sqlite3*) sqliteHandle {
41     return db;
44 - (BOOL) open {
45         int err = sqlite3_open( [databasePath fileSystemRepresentation], &db );
46         if(err != SQLITE_OK) {
47         NSLog(@"error opening!: %d", err);
48                 return NO;
49         }
50         
51         return YES;
54 - (void) close {
55     
56     [self clearCachedStatements];
57     
58         if (!db) {
59         return;
60     }
61     
62     int  rc;
63     BOOL retry;
64     int numberOfRetries = 0;
65     do {
66         retry   = NO;
67         rc      = sqlite3_close(db);
68         if (SQLITE_BUSY == rc) {
69             retry = YES;
70             usleep(20);
71             if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
72                 NSLog(@"%s:%d", __FUNCTION__, __LINE__);
73                 NSLog(@"Database busy, unable to close");
74                 return;
75             }
76         }
77         else if (SQLITE_OK != rc) {
78             NSLog(@"error closing!: %d", rc);
79         }
80     }
81     while (retry);
82     
83         db = nil;
86 - (void) clearCachedStatements {
87     
88     NSEnumerator *e = [cachedStatements objectEnumerator];
89     FMStatement *cachedStmt;
91     while ((cachedStmt = [e nextObject])) {
92         [cachedStmt close];
93     }
94     
95     [cachedStatements removeAllObjects];
98 - (FMStatement*) cachedStatementForQuery:(NSString*)query {
99     return [cachedStatements objectForKey:query];
102 - (void) setCachedStatement:(FMStatement*)statement forQuery:(NSString*)query {
103     //NSLog(@"setting query: %@", query);
104     query = [query copy]; // in case we got handed in a mutable string...
105     [statement setQuery:query];
106     [cachedStatements setObject:statement forKey:query];
107     [query release];
111 - (BOOL) rekey:(NSString*)key {
112 #ifdef SQLITE_HAS_CODEC
113     if (!key) {
114         return NO;
115     }
116     
117     int rc = sqlite3_rekey(db, [key UTF8String], strlen([key UTF8String]));
118     
119     if (rc != SQLITE_OK) {
120         NSLog(@"error on rekey: %d", rc);
121         NSLog(@"%@", [self lastErrorMessage]);
122     }
123     
124     return (rc == SQLITE_OK);
125 #else
126     return NO;
127 #endif
130 - (BOOL) setKey:(NSString*)key {
131 #ifdef SQLITE_HAS_CODEC
132     if (!key) {
133         return NO;
134     }
135     
136     int rc = sqlite3_key(db, [key UTF8String], strlen([key UTF8String]));
137     
138     return (rc == SQLITE_OK);
139 #else
140     return NO;
141 #endif
144 - (BOOL) goodConnection {
145     
146     if (!db) {
147         return NO;
148     }
149     
150     FMResultSet *rs = [self executeQuery:@"select name from sqlite_master where type='table'"];
151     
152     if (rs) {
153         [rs close];
154         return YES;
155     }
156     
157     return NO;
160 - (void) compainAboutInUse {
161     NSLog(@"The FMDatabase %@ is currently in use.", self);
162     
163     if (crashOnErrors) {
164         NSAssert1(false, @"The FMDatabase %@ is currently in use.", self);
165     }
168 - (NSString*) lastErrorMessage {
169     return [NSString stringWithUTF8String:sqlite3_errmsg(db)];
172 - (BOOL) hadError {
173     return ([self lastErrorCode] != SQLITE_OK);
176 - (int) lastErrorCode {
177     return sqlite3_errcode(db);
180 - (sqlite_int64) lastInsertRowId {
181     
182     if (inUse) {
183         [self compainAboutInUse];
184         return NO;
185     }
186     [self setInUse:YES];
187     
188     sqlite_int64 ret = sqlite3_last_insert_rowid(db);
189     
190     [self setInUse:NO];
191     
192     return ret;
195 - (void) bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt; {
196     
197     if ((!obj) || ((NSNull *)obj == [NSNull null])) {
198         sqlite3_bind_null(pStmt, idx);
199     }
200     
201     // FIXME - someday check the return codes on these binds.
202     else if ([obj isKindOfClass:[NSData class]]) {
203         sqlite3_bind_blob(pStmt, idx, [obj bytes], [obj length], SQLITE_STATIC);
204     }
205     else if ([obj isKindOfClass:[NSDate class]]) {
206         sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]);
207     }
208     else if ([obj isKindOfClass:[NSNumber class]]) {
209         
210         if (strcmp([obj objCType], @encode(BOOL)) == 0) {
211             sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0));
212         }
213         else if (strcmp([obj objCType], @encode(int)) == 0) {
214             sqlite3_bind_int64(pStmt, idx, [obj longValue]);
215         }
216         else if (strcmp([obj objCType], @encode(long)) == 0) {
217             sqlite3_bind_int64(pStmt, idx, [obj longValue]);
218         }
219         else if (strcmp([obj objCType], @encode(float)) == 0) {
220             sqlite3_bind_double(pStmt, idx, [obj floatValue]);
221         }
222         else if (strcmp([obj objCType], @encode(double)) == 0) {
223             sqlite3_bind_double(pStmt, idx, [obj doubleValue]);
224         }
225         else {
226             sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
227         }
228     }
229     else {
230         sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
231     }
234 - (id) executeQuery:(NSString *)sql arguments:(va_list)args {
235     
236     if (inUse) {
237         [self compainAboutInUse];
238         return nil;
239     }
240     
241     [self setInUse:YES];
242     
243     FMResultSet *rs = nil;
244     
245     int rc                  = 0x00;;
246     sqlite3_stmt *pStmt     = 0x00;;
247     FMStatement *statement  = 0x00;
248     
249     if (traceExecution && sql) {
250         NSLog(@"%@ executeQuery: %@", self, sql);
251     }
252     
253     if (shouldCacheStatements) {
254         statement = [self cachedStatementForQuery:sql];
255         pStmt = statement ? [statement statement] : 0x00;
256     }
257     
258     int numberOfRetries = 0;
259     BOOL retry          = NO;
260     
261     if (!pStmt) {
262         do {
263             retry   = NO;
264             rc      = sqlite3_prepare(db, [sql UTF8String], -1, &pStmt, 0);
265             
266             if (SQLITE_BUSY == rc) {
267                 retry = YES;
268                 usleep(20);
269                 
270                 if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
271                     NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
272                     NSLog(@"Database busy");
273                     sqlite3_finalize(pStmt);
274                     [self setInUse:NO];
275                     return nil;
276                 }
277             }
278             else if (SQLITE_OK != rc) {
279                 
280                 
281                 if (logsErrors) {
282                     NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
283                     NSLog(@"DB Query: %@", sql);
284                     if (crashOnErrors) {
285 #ifdef __BIG_ENDIAN__
286                         asm{ trap };
287 #endif
288                         NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
289                     }
290                 }
291                 
292                 sqlite3_finalize(pStmt);
293                 
294                 [self setInUse:NO];
295                 return nil;
296             }
297         }
298         while (retry);
299     }
300     
301     id obj;
302     int idx = 0;
303     int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!)
304     
305     while (idx < queryCount) {
306         obj = va_arg(args, id);
307         
308         if (traceExecution) {
309             NSLog(@"obj: %@", obj);
310         }
311         
312         idx++;
313         
314         [self bindObject:obj toColumn:idx inStatement:pStmt];
315     }
316     
317     if (idx != queryCount) {
318         NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)");
319         sqlite3_finalize(pStmt);
320         [self setInUse:NO];
321         return nil;
322     }
323     
324     [statement retain]; // to balance the release below
325     
326     if (!statement) {
327         statement = [[FMStatement alloc] init];
328         [statement setStatement:pStmt];
329         
330         if (shouldCacheStatements) {
331             [self setCachedStatement:statement forQuery:sql];
332         }
333     }
334     
335     // the statement gets close in rs's dealloc or [rs close];
336     rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self];
337     [rs setQuery:sql];
338     
339     statement.useCount = statement.useCount + 1;
340     
341     [statement release];    
342     
343     [self setInUse:NO];
344     
345     return rs;
348 - (id) executeQuery:(NSString*)sql, ... {
349     va_list args;
350     va_start(args, sql);
351     
352     id result = [self executeQuery:sql arguments:args];
353     
354     va_end(args);
355     return result;
359 - (BOOL) executeUpdate:(NSString*)sql arguments:(va_list)args {
360     
361     if (inUse) {
362         [self compainAboutInUse];
363         return NO;
364     }
365     
366     [self setInUse:YES];
367     
368     int rc                   = 0x00;
369     sqlite3_stmt *pStmt      = 0x00;
370     FMStatement *cachedStmt = 0x00;
371     
372     if (traceExecution && sql) {
373         NSLog(@"%@ executeUpdate: %@", self, sql);
374     }
375     
376     if (shouldCacheStatements) {
377         cachedStmt = [self cachedStatementForQuery:sql];
378         pStmt = cachedStmt ? [cachedStmt statement] : 0x00;
379     }
380     
381     int numberOfRetries = 0;
382     BOOL retry          = NO;
383     
384     if (!pStmt) {
385         
386         do {
387             retry   = NO;
388             rc      = sqlite3_prepare(db, [sql UTF8String], -1, &pStmt, 0);
389             if (SQLITE_BUSY == rc) {
390                 retry = YES;
391                 usleep(20);
392                 
393                 if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
394                     NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
395                     NSLog(@"Database busy");
396                     sqlite3_finalize(pStmt);
397                     [self setInUse:NO];
398                     return NO;
399                 }
400             }
401             else if (SQLITE_OK != rc) {
402                 
403                 
404                 if (logsErrors) {
405                     NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
406                     NSLog(@"DB Query: %@", sql);
407                     if (crashOnErrors) {
408 #ifdef __BIG_ENDIAN__
409                         asm{ trap };
410 #endif
411                         NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
412                     }
413                 }
414                 
415                 sqlite3_finalize(pStmt);
416                 [self setInUse:NO];
417                 
418                 return NO;
419             }
420         }
421         while (retry);
422     }
423     
424     
425     id obj;
426     int idx = 0;
427     int queryCount = sqlite3_bind_parameter_count(pStmt);
428     
429     while (idx < queryCount) {
430         
431         obj = va_arg(args, id);
432         
433         if (traceExecution) {
434             NSLog(@"obj: %@", obj);
435         }
436         
437         idx++;
438         
439         [self bindObject:obj toColumn:idx inStatement:pStmt];
440     }
441     
442     if (idx != queryCount) {
443         NSLog(@"Error: the bind count is not correct for the # of variables (%@) (executeUpdate)", sql);
444         sqlite3_finalize(pStmt);
445         [self setInUse:NO];
446         return NO;
447     }
448     
449     /* Call sqlite3_step() to run the virtual machine. Since the SQL being
450      ** executed is not a SELECT statement, we assume no data will be returned.
451      */
452     numberOfRetries = 0;
453     do {
454         rc      = sqlite3_step(pStmt);
455         retry   = NO;
456         
457         if (SQLITE_BUSY == rc) {
458             // this will happen if the db is locked, like if we are doing an update or insert.
459             // in that case, retry the step... and maybe wait just 10 milliseconds.
460             retry = YES;
461             usleep(20);
462             
463             if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
464                 NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
465                 NSLog(@"Database busy");
466                 retry = NO;
467             }
468         }
469         else if (SQLITE_DONE == rc || SQLITE_ROW == rc) {
470             // all is well, let's return.
471         }
472         else if (SQLITE_ERROR == rc) {
473             NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(db));
474             NSLog(@"DB Query: %@", sql);
475         }
476         else if (SQLITE_MISUSE == rc) {
477             // uh oh.
478             NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(db));
479             NSLog(@"DB Query: %@", sql);
480         }
481         else {
482             // wtf?
483             NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(db));
484             NSLog(@"DB Query: %@", sql);
485         }
486         
487     } while (retry);
488     
489     assert( rc!=SQLITE_ROW );
490     
491     
492     if (shouldCacheStatements && !cachedStmt) {
493         cachedStmt = [[FMStatement alloc] init];
494         
495         [cachedStmt setStatement:pStmt];
496         
497         [self setCachedStatement:cachedStmt forQuery:sql];
498         
499         [cachedStmt release];
500     }
501     
502     if (cachedStmt) {
503         cachedStmt.useCount = cachedStmt.useCount + 1;
504         rc = sqlite3_reset(pStmt);
505     }
506     else {
507         /* Finalize the virtual machine. This releases all memory and other
508          ** resources allocated by the sqlite3_prepare() call above.
509          */
510         rc = sqlite3_finalize(pStmt);
511     }
512     
513     [self setInUse:NO];
514     
515     return (rc == SQLITE_OK);
518 - (BOOL) executeUpdate:(NSString*)sql, ... {
519     va_list args;
520     va_start(args, sql);
521     
522     BOOL result = [self executeUpdate:sql arguments:args];
523     
524     va_end(args);
525     return result;
530 - (BOOL) rollback {
531     BOOL b = [self executeUpdate:@"ROLLBACK TRANSACTION;"];
532     if (b) {
533         inTransaction = NO;
534     }
535     return b;
538 - (BOOL) commit {
539     BOOL b =  [self executeUpdate:@"COMMIT TRANSACTION;"];
540     if (b) {
541         inTransaction = NO;
542     }
543     return b;
546 - (BOOL) beginDeferredTransaction {
547     BOOL b =  [self executeUpdate:@"BEGIN DEFERRED TRANSACTION;"];
548     if (b) {
549         inTransaction = YES;
550     }
551     return b;
554 - (BOOL) beginTransaction {
555     BOOL b =  [self executeUpdate:@"BEGIN EXCLUSIVE TRANSACTION;"];
556     if (b) {
557         inTransaction = YES;
558     }
559     return b;
562 - (BOOL)logsErrors {
563     return logsErrors;
565 - (void)setLogsErrors:(BOOL)flag {
566     logsErrors = flag;
569 - (BOOL)crashOnErrors {
570     return crashOnErrors;
572 - (void)setCrashOnErrors:(BOOL)flag {
573     crashOnErrors = flag;
576 - (BOOL)inUse {
577     return inUse || inTransaction;
580 - (void) setInUse:(BOOL)b {
581     inUse = b;
584 - (BOOL)inTransaction {
585     return inTransaction;
587 - (void)setInTransaction:(BOOL)flag {
588     inTransaction = flag;
591 - (BOOL)traceExecution {
592     return traceExecution;
594 - (void)setTraceExecution:(BOOL)flag {
595     traceExecution = flag;
598 - (BOOL)checkedOut {
599     return checkedOut;
601 - (void)setCheckedOut:(BOOL)flag {
602     checkedOut = flag;
606 - (int)busyRetryTimeout {
607     return busyRetryTimeout;
609 - (void)setBusyRetryTimeout:(int)newBusyRetryTimeout {
610     busyRetryTimeout = newBusyRetryTimeout;
614 - (BOOL)shouldCacheStatements {
615     return shouldCacheStatements;
618 - (void)setShouldCacheStatements:(BOOL)value {
619     
620     shouldCacheStatements = value;
621     
622     if (shouldCacheStatements && !cachedStatements) {
623         [self setCachedStatements:[NSMutableDictionary dictionary]];
624     }
625     
626     if (!shouldCacheStatements) {
627         [self setCachedStatements:nil];
628     }
631 - (NSMutableDictionary *) cachedStatements {
632     return cachedStatements;
635 - (void)setCachedStatements:(NSMutableDictionary *)value {
636     if (cachedStatements != value) {
637         [cachedStatements release];
638         cachedStatements = [value retain];
639     }
643 @end
647 @implementation FMStatement
649 - (void)dealloc {
650         [self close];
651     [query release];
652         [super dealloc];
656 - (void) close {
657     if (statement) {
658         sqlite3_finalize(statement);
659         statement = 0x00;
660     }
663 - (void) reset {
664     if (statement) {
665         sqlite3_reset(statement);
666     }
669 - (sqlite3_stmt *) statement {
670     return statement;
673 - (void)setStatement:(sqlite3_stmt *)value {
674     statement = value;
677 - (NSString *) query {
678     return query;
681 - (void)setQuery:(NSString *)value {
682     if (query != value) {
683         [query release];
684         query = [value retain];
685     }
688 - (long)useCount {
689     return useCount;
692 - (void)setUseCount:(long)value {
693     if (useCount != value) {
694         useCount = value;
695     }
700 - (NSString*) description {
701     return [NSString stringWithFormat:@"%@ %d hit(s) for query %@", [super description], useCount, query];
705 @end