PageRenderTime 75ms CodeModel.GetById 17ms app.highlight 54ms RepoModel.GetById 1ms app.codeStats 0ms

/fmdb/FMResultSet.m

http://github.com/petewarden/iPhoneTracker
Objective C | 405 lines | 281 code | 113 blank | 11 comment | 68 complexity | a6d5b7f2ec57522ef802908647bbdfb5 MD5 | raw file
  1#import "FMResultSet.h"
  2#import "FMDatabase.h"
  3#import "unistd.h"
  4
  5@interface FMDatabase ()
  6- (void)resultSetDidClose:(FMResultSet *)resultSet;
  7@end
  8
  9
 10@interface FMResultSet (Private)
 11- (NSMutableDictionary *)columnNameToIndexMap;
 12- (void)setColumnNameToIndexMap:(NSMutableDictionary *)value;
 13@end
 14
 15@implementation FMResultSet
 16
 17+ (id)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB {
 18    
 19    FMResultSet *rs = [[FMResultSet alloc] init];
 20    
 21    [rs setStatement:statement];
 22    [rs setParentDB:aDB];
 23    
 24    return [rs autorelease];
 25}
 26
 27- (void)finalize {
 28    [self close];
 29    [super finalize];
 30}
 31
 32- (void)dealloc {
 33    [self close];
 34    
 35    [query release];
 36    query = nil;
 37    
 38    [columnNameToIndexMap release];
 39    columnNameToIndexMap = nil;
 40    
 41    [super dealloc];
 42}
 43
 44- (void)close {
 45    [statement reset];
 46    [statement release];
 47    statement = nil;
 48    
 49    // we don't need this anymore... (i think)
 50    //[parentDB setInUse:NO];
 51    [parentDB resultSetDidClose:self];
 52    parentDB = nil;
 53}
 54
 55- (void)setupColumnNames {
 56    
 57    if (!columnNameToIndexMap) {
 58        [self setColumnNameToIndexMap:[NSMutableDictionary dictionary]];
 59    }    
 60    
 61    int columnCount = sqlite3_column_count(statement.statement);
 62    
 63    int columnIdx = 0;
 64    for (columnIdx = 0; columnIdx < columnCount; columnIdx++) {
 65        [columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx]
 66                                 forKey:[[NSString stringWithUTF8String:sqlite3_column_name(statement.statement, columnIdx)] lowercaseString]];
 67    }
 68    columnNamesSetup = YES;
 69}
 70
 71- (void)kvcMagic:(id)object {
 72    
 73    int columnCount = sqlite3_column_count(statement.statement);
 74    
 75    int columnIdx = 0;
 76    for (columnIdx = 0; columnIdx < columnCount; columnIdx++) {
 77        
 78        const char *c = (const char *)sqlite3_column_text(statement.statement, columnIdx);
 79        
 80        // check for a null row
 81        if (c) {
 82            NSString *s = [NSString stringWithUTF8String:c];
 83            
 84            [object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name(statement.statement, columnIdx)]];
 85        }
 86    }
 87}
 88
 89- (NSDictionary *)resultDict {
 90    
 91    int num_cols = sqlite3_data_count(statement.statement);
 92    
 93    if (num_cols > 0) {
 94        NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols];
 95        
 96        if (!columnNamesSetup) {
 97            [self setupColumnNames];
 98        }
 99        
100        NSEnumerator *columnNames = [columnNameToIndexMap keyEnumerator];
101        NSString *columnName = nil;
102        while ((columnName = [columnNames nextObject])) {
103            id objectValue = [self objectForColumnName:columnName];
104            [dict setObject:objectValue forKey:columnName];
105        }
106        
107        return [[dict copy] autorelease];
108    }
109    else {
110        NSLog(@"Warning: There seem to be no columns in this set.");
111    }
112    
113    return nil;
114}
115
116- (BOOL)next {
117    
118    int rc;
119    BOOL retry;
120    int numberOfRetries = 0;
121    do {
122        retry = NO;
123        
124        rc = sqlite3_step(statement.statement);
125        
126        if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
127            // this will happen if the db is locked, like if we are doing an update or insert.
128            // in that case, retry the step... and maybe wait just 10 milliseconds.
129            retry = YES;
130            if (SQLITE_LOCKED == rc) {
131                rc = sqlite3_reset(statement.statement);
132                if (rc != SQLITE_LOCKED) {
133                    NSLog(@"Unexpected result from sqlite3_reset (%d) rs", rc);
134                }
135            }
136            usleep(20);
137            
138            if ([parentDB busyRetryTimeout] && (numberOfRetries++ > [parentDB busyRetryTimeout])) {
139                
140                NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [parentDB databasePath]);
141                NSLog(@"Database busy");
142                break;
143            }
144        }
145        else if (SQLITE_DONE == rc || SQLITE_ROW == rc) {
146            // all is well, let's return.
147        }
148        else if (SQLITE_ERROR == rc) {
149            NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle]));
150            break;
151        } 
152        else if (SQLITE_MISUSE == rc) {
153            // uh oh.
154            NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle]));
155            break;
156        }
157        else {
158            // wtf?
159            NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle]));
160            break;
161        }
162        
163    } while (retry);
164    
165    
166    if (rc != SQLITE_ROW) {
167        [self close];
168    }
169    
170    return (rc == SQLITE_ROW);
171}
172
173- (BOOL)hasAnotherRow {
174    return sqlite3_errcode([parentDB sqliteHandle]) == SQLITE_ROW;
175}
176
177- (int)columnIndexForName:(NSString*)columnName {
178    
179    if (!columnNamesSetup) {
180        [self setupColumnNames];
181    }
182    
183    columnName = [columnName lowercaseString];
184    
185    NSNumber *n = [columnNameToIndexMap objectForKey:columnName];
186    
187    if (n) {
188        return [n intValue];
189    }
190    
191    NSLog(@"Warning: I could not find the column named '%@'.", columnName);
192    
193    return -1;
194}
195
196
197
198- (int)intForColumn:(NSString*)columnName {
199    return [self intForColumnIndex:[self columnIndexForName:columnName]];
200}
201
202- (int)intForColumnIndex:(int)columnIdx {
203    return sqlite3_column_int(statement.statement, columnIdx);
204}
205
206- (long)longForColumn:(NSString*)columnName {
207    return [self longForColumnIndex:[self columnIndexForName:columnName]];
208}
209
210- (long)longForColumnIndex:(int)columnIdx {
211    return (long)sqlite3_column_int64(statement.statement, columnIdx);
212}
213
214- (long long int)longLongIntForColumn:(NSString*)columnName {
215    return [self longLongIntForColumnIndex:[self columnIndexForName:columnName]];
216}
217
218- (long long int)longLongIntForColumnIndex:(int)columnIdx {
219    return sqlite3_column_int64(statement.statement, columnIdx);
220}
221
222- (BOOL)boolForColumn:(NSString*)columnName {
223    return [self boolForColumnIndex:[self columnIndexForName:columnName]];
224}
225
226- (BOOL)boolForColumnIndex:(int)columnIdx {
227    return ([self intForColumnIndex:columnIdx] != 0);
228}
229
230- (double)doubleForColumn:(NSString*)columnName {
231    return [self doubleForColumnIndex:[self columnIndexForName:columnName]];
232}
233
234- (double)doubleForColumnIndex:(int)columnIdx {
235    return sqlite3_column_double(statement.statement, columnIdx);
236}
237
238- (NSString*)stringForColumnIndex:(int)columnIdx {
239    
240    if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
241        return nil;
242    }
243    
244    const char *c = (const char *)sqlite3_column_text(statement.statement, columnIdx);
245    
246    if (!c) {
247        // null row.
248        return nil;
249    }
250    
251    return [NSString stringWithUTF8String:c];
252}
253
254- (NSString*)stringForColumn:(NSString*)columnName {
255    return [self stringForColumnIndex:[self columnIndexForName:columnName]];
256}
257
258- (NSDate*)dateForColumn:(NSString*)columnName {
259    return [self dateForColumnIndex:[self columnIndexForName:columnName]];
260}
261
262- (NSDate*)dateForColumnIndex:(int)columnIdx {
263    
264    if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
265        return nil;
266    }
267    
268    return [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]];
269}
270
271
272- (NSData*)dataForColumn:(NSString*)columnName {
273    return [self dataForColumnIndex:[self columnIndexForName:columnName]];
274}
275
276- (NSData*)dataForColumnIndex:(int)columnIdx {
277    
278    if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
279        return nil;
280    }
281    
282    int dataSize = sqlite3_column_bytes(statement.statement, columnIdx);
283    
284    NSMutableData *data = [NSMutableData dataWithLength:dataSize];
285    
286    memcpy([data mutableBytes], sqlite3_column_blob(statement.statement, columnIdx), dataSize);
287    
288    return data;
289}
290
291
292- (NSData*)dataNoCopyForColumn:(NSString*)columnName {
293    return [self dataNoCopyForColumnIndex:[self columnIndexForName:columnName]];
294}
295
296- (NSData*)dataNoCopyForColumnIndex:(int)columnIdx {
297    
298    if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
299        return nil;
300    }
301    
302    int dataSize = sqlite3_column_bytes(statement.statement, columnIdx);
303    
304    NSData *data = [NSData dataWithBytesNoCopy:(void *)sqlite3_column_blob(statement.statement, columnIdx) length:dataSize freeWhenDone:NO];
305    
306    return data;
307}
308
309
310- (BOOL)columnIndexIsNull:(int)columnIdx {
311    return sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL;
312}
313
314- (BOOL)columnIsNull:(NSString*)columnName {
315    return [self columnIndexIsNull:[self columnIndexForName:columnName]];
316}
317
318- (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx {
319    
320    if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) {
321        return nil;
322    }
323    
324    return sqlite3_column_text(statement.statement, columnIdx);
325}
326
327- (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName {
328    return [self UTF8StringForColumnIndex:[self columnIndexForName:columnName]];
329}
330
331- (id)objectForColumnIndex:(int)columnIdx {
332    int columnType = sqlite3_column_type(statement.statement, columnIdx);
333    
334    id returnValue = nil;
335    
336    if (columnType == SQLITE_INTEGER) {
337        returnValue = [NSNumber numberWithLongLong:[self longLongIntForColumnIndex:columnIdx]];
338    }
339    else if (columnType == SQLITE_FLOAT) {
340        returnValue = [NSNumber numberWithDouble:[self doubleForColumnIndex:columnIdx]];
341    }
342    else if (columnType == SQLITE_BLOB) {
343        returnValue = [self dataForColumnIndex:columnIdx];
344    }
345    else {
346        //default to a string for everything else
347        returnValue = [self stringForColumnIndex:columnIdx];
348    }
349    
350    if (returnValue == nil) {
351        returnValue = [NSNull null];
352    }
353    
354    return returnValue;
355}
356
357- (id)objectForColumnName:(NSString*)columnName {
358    return [self objectForColumnIndex:[self columnIndexForName:columnName]];
359}
360
361
362// returns autoreleased NSString containing the name of the column in the result set
363- (NSString*)columnNameForIndex:(int)columnIdx {
364    return [NSString stringWithUTF8String: sqlite3_column_name(statement.statement, columnIdx)];
365}
366
367- (void)setParentDB:(FMDatabase *)newDb {
368    parentDB = newDb;
369}
370
371
372- (NSString *)query {
373    return query;
374}
375
376- (void)setQuery:(NSString *)value {
377    [value retain];
378    [query release];
379    query = value;
380}
381
382- (NSMutableDictionary *)columnNameToIndexMap {
383    return columnNameToIndexMap;
384}
385
386- (void)setColumnNameToIndexMap:(NSMutableDictionary *)value {
387    [value retain];
388    [columnNameToIndexMap release];
389    columnNameToIndexMap = value;
390}
391
392- (FMStatement *)statement {
393    return statement;
394}
395
396- (void)setStatement:(FMStatement *)value {
397    if (statement != value) {
398        [statement release];
399        statement = [value retain];
400    }
401}
402
403
404
405@end