nano-store 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.gitignore +3 -0
  2. data/.gitmodules +3 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +25 -0
  6. data/README.md +193 -0
  7. data/Rakefile +1 -0
  8. data/app/app_delegate.rb +5 -0
  9. data/lib/nano-store.rb +1 -0
  10. data/lib/nano_store/bag.rb +98 -0
  11. data/lib/nano_store/model.rb +142 -0
  12. data/lib/nano_store/nano_store.rb +36 -0
  13. data/lib/nano_store/store_extension.rb +150 -0
  14. data/lib/nano_store/version.rb +3 -0
  15. data/lib/nano_store.rb +14 -0
  16. data/nano-store.gemspec +17 -0
  17. data/resources/.gitignore +0 -0
  18. data/spec/bag_spec.rb +66 -0
  19. data/spec/model_spec.rb +130 -0
  20. data/spec/nano_store_spec.rb +48 -0
  21. data/spec/store_extension_spec.rb +110 -0
  22. data/vendor/NanoStore/Classes/Advanced/NSFNanoEngine.h +542 -0
  23. data/vendor/NanoStore/Classes/Advanced/NSFNanoEngine.m +1781 -0
  24. data/vendor/NanoStore/Classes/Advanced/NSFNanoResult.h +137 -0
  25. data/vendor/NanoStore/Classes/Advanced/NSFNanoResult.m +265 -0
  26. data/vendor/NanoStore/Classes/Private/NSFNanoBag_Private.h +37 -0
  27. data/vendor/NanoStore/Classes/Private/NSFNanoEngine_Private.h +69 -0
  28. data/vendor/NanoStore/Classes/Private/NSFNanoExpression_Private.h +35 -0
  29. data/vendor/NanoStore/Classes/Private/NSFNanoGlobals_Private.h +99 -0
  30. data/vendor/NanoStore/Classes/Private/NSFNanoObject_Private.h +35 -0
  31. data/vendor/NanoStore/Classes/Private/NSFNanoPredicate_Private.h +35 -0
  32. data/vendor/NanoStore/Classes/Private/NSFNanoResult_Private.h +43 -0
  33. data/vendor/NanoStore/Classes/Private/NSFNanoSearch_Private.h +48 -0
  34. data/vendor/NanoStore/Classes/Private/NSFNanoStore_Private.h +57 -0
  35. data/vendor/NanoStore/Classes/Private/NanoStore_Private.h +37 -0
  36. data/vendor/NanoStore/Classes/Public/NSFNanoBag.h +306 -0
  37. data/vendor/NanoStore/Classes/Public/NSFNanoBag.m +485 -0
  38. data/vendor/NanoStore/Classes/Public/NSFNanoExpression.h +125 -0
  39. data/vendor/NanoStore/Classes/Public/NSFNanoExpression.m +103 -0
  40. data/vendor/NanoStore/Classes/Public/NSFNanoGlobals.h +323 -0
  41. data/vendor/NanoStore/Classes/Public/NSFNanoGlobals.m +145 -0
  42. data/vendor/NanoStore/Classes/Public/NSFNanoObject.h +298 -0
  43. data/vendor/NanoStore/Classes/Public/NSFNanoObject.m +187 -0
  44. data/vendor/NanoStore/Classes/Public/NSFNanoObjectProtocol.h +119 -0
  45. data/vendor/NanoStore/Classes/Public/NSFNanoPredicate.h +123 -0
  46. data/vendor/NanoStore/Classes/Public/NSFNanoPredicate.m +130 -0
  47. data/vendor/NanoStore/Classes/Public/NSFNanoSearch.h +381 -0
  48. data/vendor/NanoStore/Classes/Public/NSFNanoSearch.m +835 -0
  49. data/vendor/NanoStore/Classes/Public/NSFNanoSortDescriptor.h +124 -0
  50. data/vendor/NanoStore/Classes/Public/NSFNanoSortDescriptor.m +79 -0
  51. data/vendor/NanoStore/Classes/Public/NSFNanoStore.h +475 -0
  52. data/vendor/NanoStore/Classes/Public/NSFNanoStore.m +1375 -0
  53. data/vendor/NanoStore/Classes/Public/NanoStore.h +463 -0
  54. data/vendor/NanoStore/LICENSE +25 -0
  55. data/vendor/NanoStore/NanoStore.bridgesupport +1215 -0
  56. data/vendor/NanoStore/README.md +411 -0
  57. metadata +118 -0
@@ -0,0 +1,1781 @@
1
+ /*
2
+ NSFNanoEngine.m
3
+ NanoStore
4
+
5
+ Copyright (c) 2010 Webbo, L.L.C. All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without modification, are permitted
8
+ provided that the following conditions are met:
9
+
10
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
11
+ and the following disclaimer.
12
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
13
+ and the following disclaimer in the documentation and/or other materials provided with the distribution.
14
+ * Neither the name of Webbo nor the names of its contributors may be used to endorse or promote
15
+ products derived from this software without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
20
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
+ SUCH DAMAGE.
25
+ */
26
+
27
+ #import "NanoStore.h"
28
+ #import "NanoStore_Private.h"
29
+
30
+ #import <stdio.h>
31
+ #import <stdlib.h>
32
+ #import <unistd.h>
33
+
34
+ #pragma mark// ==================================
35
+ #pragma mark// NSFNanoEngine C Declarations
36
+ #pragma mark// ==================================
37
+
38
+ int NSFP_commitCallback(void* nsfdb);
39
+
40
+ static char __NSFP_base64Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
41
+ static NSArray *__NSFP_SQLCommandsReturningData = nil;
42
+ static NSArray *__NSFPSharedROWIDKeywords = nil;
43
+ static NSSet *__NSFPSharedNanoStoreEngineDatatypes = nil;
44
+
45
+ #pragma mark -
46
+
47
+ @implementation NSFNanoEngine
48
+ {
49
+ @protected
50
+ sqlite3 *sqlite;
51
+ NSString *path;
52
+ NSFCacheMethod cacheMethod;
53
+
54
+ /** \cond */
55
+ NSMutableDictionary *schema;
56
+ BOOL willCommitChangeSchema;
57
+ unsigned int busyTimeout;
58
+ /** \endcond */
59
+ }
60
+
61
+ @synthesize sqlite;
62
+ @synthesize path;
63
+ @synthesize cacheMethod;
64
+
65
+ #pragma mark -
66
+
67
+ #pragma mark// ==================================
68
+ #pragma mark// Initialization/Cleanup Methods
69
+ #pragma mark// ==================================
70
+
71
+ + (id)databaseWithPath:(NSString *)thePath
72
+ {
73
+ if (nil == thePath)
74
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
75
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: thePath is nil.", [self class], _cmd]
76
+ userInfo:nil]raise];
77
+
78
+ return [[self alloc]initWithPath:thePath];
79
+ }
80
+
81
+ - (id)initWithPath:(NSString *)thePath
82
+ {
83
+ if (nil == thePath)
84
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
85
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: thePath is nil.", [self class], _cmd]
86
+ userInfo:nil]raise];
87
+
88
+ if ((self = [self init])) {
89
+ path = [thePath copy];
90
+ }
91
+
92
+ return self;
93
+ }
94
+
95
+ /** \cond */
96
+
97
+ + (void)initialize
98
+ {
99
+ __NSFP_SQLCommandsReturningData = [[NSArray alloc]initWithObjects:@"SELECT", @"PRAGMA", @"EXPLAIN", nil];
100
+ }
101
+
102
+ - (id)init
103
+ {
104
+ if ((self = [super init])) {
105
+ path = nil;
106
+ schema = nil;
107
+ }
108
+ return self;
109
+ }
110
+
111
+ - (void)dealloc
112
+ {
113
+ [self close];
114
+
115
+
116
+ }
117
+
118
+ /** \endcond */
119
+
120
+ - (NSString*)description
121
+ {
122
+ return [self NSFP_nestedDescriptionWithPrefixedSpace:@""];
123
+ }
124
+
125
+ #pragma mark// ==================================
126
+ #pragma mark// Opening & Closing Methods
127
+ #pragma mark// ==================================
128
+
129
+ - (BOOL)openWithCacheMethod:(NSFCacheMethod)theCacheMethod useFastMode:(BOOL)useFastMode
130
+ {
131
+ int status = sqlite3_open_v2( [path UTF8String], &sqlite,
132
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_AUTOPROXY | SQLITE_OPEN_FULLMUTEX, NULL);
133
+
134
+ // Set NanoStoreEngine's page size to match the system current page size
135
+ if (0 == [[self tables]count]) {
136
+ NSUInteger systemPageSize = [NSFNanoEngine systemPageSize];
137
+ [self setPageSize:systemPageSize];
138
+ }
139
+
140
+ // Since we're operating with extended result code support, extract the bits
141
+ // and obtain the regular result code
142
+ // For more info check: http://www.sqlite.org/c3ref/c_ioerr_access.html
143
+
144
+ status = [NSFNanoEngine NSFP_stripBitsFromExtendedResultCode:status];
145
+
146
+ if ((SQLITE_OK != status) || (sqlite3_extended_result_codes(self.sqlite, 1) != SQLITE_OK))
147
+ return NO;
148
+
149
+ if ([[path lowercaseString]isEqualToString:NSFMemoryDatabase] == YES) {
150
+
151
+ sqlite3_exec(self.sqlite, "PRAGMA fullfsync = OFF;", NULL, NULL, NULL);
152
+ sqlite3_exec(self.sqlite, "PRAGMA temp_store = MEMORY", NULL, NULL, NULL);
153
+ sqlite3_exec(self.sqlite, "PRAGMA synchronous = OFF;", NULL, NULL, NULL);
154
+ sqlite3_exec(self.sqlite, "PRAGMA journal_mode = MEMORY;", NULL, NULL, NULL);
155
+ sqlite3_exec(self.sqlite, "PRAGMA temp_store = MEMORY", NULL, NULL, NULL);
156
+
157
+ } else {
158
+
159
+ // Set FastMode accordingly...
160
+ if (YES == useFastMode) {
161
+ sqlite3_exec(self.sqlite, "PRAGMA fullfsync = OFF;", NULL, NULL, NULL);
162
+ sqlite3_exec(self.sqlite, "PRAGMA synchronous = OFF;", NULL, NULL, NULL);
163
+ sqlite3_exec(self.sqlite, "PRAGMA journal_mode = MEMORY;", NULL, NULL, NULL);
164
+ sqlite3_exec(self.sqlite, "PRAGMA temp_store = MEMORY", NULL, NULL, NULL);
165
+ } else {
166
+ sqlite3_exec(self.sqlite, "PRAGMA fullfsync = OFF;", NULL, NULL, NULL);
167
+ sqlite3_exec(self.sqlite, "PRAGMA synchronous = FULL;", NULL, NULL, NULL);
168
+ sqlite3_exec(self.sqlite, "PRAGMA journal_mode = DELETE", NULL, NULL, NULL);
169
+ sqlite3_exec(self.sqlite, "PRAGMA temp_store = DEFAULT", NULL, NULL, NULL);
170
+ }
171
+
172
+ }
173
+
174
+ // Save whether we want data to be fetched lazily
175
+ cacheMethod = theCacheMethod;
176
+
177
+ [self setBusyTimeout:250];
178
+
179
+ // Refresh the schema cache
180
+ [self NSFP_rebuildDatatypeCache];
181
+
182
+ [self NSFP_installCommitCallback];
183
+
184
+ return YES;
185
+ }
186
+
187
+
188
+ - (BOOL)close
189
+ {
190
+ if (NO == self.sqlite) {
191
+ return NO;
192
+ }
193
+
194
+ if (YES == [self isTransactionActive]) {
195
+ [self rollbackTransaction];
196
+ }
197
+
198
+ // Make sure we clear the temporary data from schema
199
+ NSArray *tempTables = [self temporaryTables];
200
+
201
+ if ([tempTables count] > 0) {
202
+ [self beginTransaction];
203
+
204
+ for (NSString *table in tempTables) {
205
+ [self dropTable:table];
206
+ }
207
+
208
+ [self commitTransaction];
209
+ }
210
+
211
+ int status = sqlite3_close(self.sqlite);
212
+ sqlite = NULL;
213
+
214
+ // Since we're operating with extended result code support, extract the bits
215
+ // and obtain the regular result code
216
+ // For more info check: http://www.sqlite.org/c3ref/c_ioerr_access.html
217
+
218
+ status = [NSFNanoEngine NSFP_stripBitsFromExtendedResultCode:status];
219
+
220
+ return (SQLITE_OK == status);
221
+ }
222
+
223
+ - (BOOL)isDatabaseOpen
224
+ {
225
+ return (NULL != self.sqlite);
226
+ }
227
+
228
+ #pragma mark Transaction Methods
229
+
230
+ - (BOOL)beginTransaction
231
+ {
232
+ if (YES == [self isTransactionActive])
233
+ return NO;
234
+
235
+ willCommitChangeSchema = NO;
236
+
237
+ return [self beginDeferredTransaction];
238
+ }
239
+
240
+ - (BOOL)beginDeferredTransaction
241
+ {
242
+ if (YES == [self isTransactionActive])
243
+ return NO;
244
+
245
+ willCommitChangeSchema = NO;
246
+
247
+ return [self NSFP_beginTransactionMode:@"BEGIN DEFERRED TRANSACTION;"];
248
+ }
249
+
250
+ - (BOOL)commitTransaction
251
+ {
252
+ if (NO == [self isTransactionActive]) {
253
+ willCommitChangeSchema = NO;
254
+ return NO;
255
+ }
256
+
257
+ if (NO == willCommitChangeSchema)
258
+ [self NSFP_uninstallCommitCallback];
259
+
260
+ BOOL success = (nil == [[self executeSQL:@"COMMIT TRANSACTION;"]error]);
261
+
262
+ if (NO == willCommitChangeSchema)
263
+ [self NSFP_installCommitCallback];
264
+
265
+ willCommitChangeSchema = NO;
266
+
267
+ return success;
268
+ }
269
+
270
+ - (BOOL)rollbackTransaction
271
+ {
272
+ if ([self isTransactionActive] == NO) {
273
+ willCommitChangeSchema = NO;
274
+ return NO;
275
+ }
276
+
277
+ BOOL success = (nil == [[self executeSQL:@"ROLLBACK TRANSACTION;"]error]);
278
+
279
+ willCommitChangeSchema = NO;
280
+
281
+ return success;
282
+ }
283
+
284
+ - (BOOL)isTransactionActive
285
+ {
286
+ sqlite3* myDB = self.sqlite;
287
+
288
+ int status = sqlite3_get_autocommit(myDB);
289
+
290
+ // Since we're operating with extended result code support, extract the bits
291
+ // and obtain the regular result code
292
+ // For more info check: http://www.sqlite.org/c3ref/c_ioerr_access.html
293
+
294
+ status = [NSFNanoEngine NSFP_stripBitsFromExtendedResultCode:status];
295
+
296
+ return (0 == status);
297
+ }
298
+
299
+ #pragma mark// ==================================
300
+ #pragma mark// Utility Methods
301
+ #pragma mark// ==================================
302
+
303
+ + (NSString *)stringWithUUID
304
+ {
305
+ CFUUIDRef uuidCF = CFUUIDCreate(NULL);
306
+ NSString *uuid = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, uuidCF);
307
+ CFRelease(uuidCF);
308
+ return uuid;
309
+ }
310
+
311
+ - (BOOL)compact
312
+ {
313
+ if (NO == [self isTransactionActive])
314
+ return (nil == [[self executeSQL:@"VACUUM;"]error]);
315
+
316
+ return NO;
317
+ }
318
+
319
+ - (BOOL)integrityCheck
320
+ {
321
+ if (NO == [self isTransactionActive]) {
322
+ NSFNanoResult* result = [self executeSQL:@"PRAGMA integrity_check"];
323
+
324
+ // SQLite returns the status as 'ok'. Let's code defensively and lowercase the result.
325
+ return ([[[[result valuesForColumn:@"integrity_check"]lastObject]lowercaseString]isEqualToString:@"ok"]);
326
+ }
327
+
328
+ return NO;
329
+ }
330
+
331
+ + (NSString *)nanoStoreEngineVersion
332
+ {
333
+ return NSFVersionKey;
334
+ }
335
+
336
+ + (NSString *)sqliteVersion
337
+ {
338
+ return [NSString stringWithUTF8String: sqlite3_libversion()];
339
+ }
340
+
341
+ + (NSSet*)sharedNanoStoreEngineDatatypes
342
+ {
343
+ if (nil == __NSFPSharedNanoStoreEngineDatatypes)
344
+ __NSFPSharedNanoStoreEngineDatatypes = [[NSSet alloc]initWithObjects:NSFStringFromNanoDataType(NSFNanoTypeRowUID),
345
+ NSFStringFromNanoDataType(NSFNanoTypeString),
346
+ NSFStringFromNanoDataType(NSFNanoTypeData),
347
+ NSFStringFromNanoDataType(NSFNanoTypeDate),
348
+ NSFStringFromNanoDataType(NSFNanoTypeNumber),
349
+ nil];
350
+
351
+ return __NSFPSharedNanoStoreEngineDatatypes;
352
+ }
353
+
354
+ #pragma mark// ==================================
355
+ #pragma mark// Table and Introspection Methods
356
+ #pragma mark// ==================================
357
+
358
+ - (BOOL)createTable:(NSString *)table withColumns:(NSArray *)columns datatypes:(NSArray *)datatypes
359
+ {
360
+ if (nil == table)
361
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
362
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: table is nil.", [self class], _cmd]
363
+ userInfo:nil]raise];
364
+
365
+ if (nil == columns)
366
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
367
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: columns is nil.", [self class], _cmd]
368
+ userInfo:nil]raise];
369
+
370
+ if (nil == datatypes)
371
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
372
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: datatypes is nil.", [self class], _cmd]
373
+ userInfo:nil]raise];
374
+
375
+ return [self NSFP_createTable:table withColumns:columns datatypes:datatypes isTemporary:NO];
376
+ }
377
+
378
+ - (BOOL)dropTable:(NSString *)table
379
+ {
380
+ if (nil == table)
381
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
382
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: table is nil.", [self class], _cmd]
383
+ userInfo:nil]raise];
384
+
385
+ BOOL transactionSetHere = NO;
386
+ if ([self isTransactionActive] == NO)
387
+ transactionSetHere = [self beginTransaction];
388
+
389
+ NSString *theSQLStatement = [[NSString alloc]initWithFormat:@"DROP TABLE %@;", table];
390
+ BOOL everythingIsFine = (nil == [[self executeSQL:theSQLStatement]error]);
391
+
392
+ if (everythingIsFine) {
393
+ theSQLStatement = [[NSString alloc]initWithFormat:@"DELETE FROM %@ WHERE %@ = '%@';", NSFP_SchemaTable, NSFP_TableIdentifier, table];
394
+ everythingIsFine = (nil == [[self executeSQL:theSQLStatement]error]);
395
+ }
396
+
397
+ if (transactionSetHere) {
398
+ if (everythingIsFine)
399
+ [self commitTransaction];
400
+ else
401
+ [self rollbackTransaction];
402
+ }
403
+
404
+ if (everythingIsFine)
405
+ [self NSFP_rebuildDatatypeCache];
406
+
407
+ return everythingIsFine;
408
+ }
409
+
410
+ - (BOOL)createIndexForColumn:(NSString *)column table:(NSString *)table isUnique:(BOOL)flag
411
+ {
412
+ if (nil == column)
413
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
414
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: column is nil.", [self class], _cmd]
415
+ userInfo:nil]raise];
416
+ if (nil == table)
417
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
418
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: table is nil.", [self class], _cmd]
419
+ userInfo:nil]raise];
420
+
421
+ NSString *theSQLStatement = nil;
422
+
423
+ if (flag)
424
+ theSQLStatement = [[NSString alloc]initWithFormat:@"CREATE UNIQUE INDEX %@_%@_IDX ON %@ (%@);", table, column, table, column];
425
+ else
426
+ theSQLStatement = [[NSString alloc]initWithFormat:@"CREATE INDEX %@_%@_IDX ON %@ (%@);", table, column, table, column];
427
+
428
+ BOOL indexWasCreated = (nil == [[self executeSQL:theSQLStatement]error]);
429
+
430
+ return indexWasCreated;
431
+ }
432
+
433
+ - (void)dropIndex:(NSString *)indexName
434
+ {
435
+ if (nil == indexName)
436
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
437
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: indexName is nil.", [self class], _cmd]
438
+ userInfo:nil]raise];
439
+
440
+ NSString *theSQLStatement = [[NSString alloc]initWithFormat:@"DROP INDEX %@;", indexName];
441
+
442
+ [self executeSQL:theSQLStatement];
443
+ }
444
+
445
+ - (NSArray *)tables
446
+ {
447
+ NSArray *allTables = [self NSFP_flattenAllTables];
448
+ if ([allTables count] == 0)
449
+ return allTables;
450
+
451
+ // Remove NSF's private table
452
+ NSMutableArray *tempTables = [NSMutableArray arrayWithArray:allTables];
453
+ [tempTables removeObject:NSFP_SchemaTable];
454
+
455
+ return tempTables;
456
+ }
457
+
458
+ - (NSDictionary *)allTables
459
+ {
460
+ NSMutableDictionary *allTables = [NSMutableDictionary dictionary];
461
+
462
+ // Make sure we obtain full column names
463
+ [self NSFP_setFullColumnNamesEnabled];
464
+
465
+ NSFNanoResult *databasesResult = [self executeSQL:@"PRAGMA database_list"];
466
+ NSArray *databases = [databasesResult valuesForColumn:@"name"];
467
+
468
+ for (NSString *database in databases) {
469
+ NSString *theSQLStatement = [NSString stringWithFormat:@"SELECT * FROM %@.sqlite_master;", database];
470
+ NSFNanoResult* result = [self executeSQL:theSQLStatement];
471
+ if (nil == [result error]) {
472
+ // Get all tables in the database
473
+ NSArray *databaseTables = [result valuesForColumn:@"sqlite_master.tbl_name"];
474
+ NSSet *tablesPerDatabase = [NSSet setWithArray:databaseTables];
475
+ [allTables setObject: [tablesPerDatabase allObjects] forKey: database];
476
+ }
477
+ }
478
+
479
+ return allTables;
480
+ }
481
+
482
+ - (NSArray *)columnsForTable:(NSString *)table
483
+ {
484
+ if (nil == table)
485
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
486
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: table is nil.", [self class], _cmd]
487
+ userInfo:nil]raise];
488
+
489
+ NSString *theSQLStatement = nil;
490
+ NSString *database = [self NSFP_prefixWithDotDelimiter:table];
491
+
492
+ if ([database isEqualToString:table] == NO) {
493
+ database = [NSString stringWithFormat:@"%@.", database];
494
+ table = [self NSFP_suffixWithDotDelimiter:table];
495
+ theSQLStatement = [NSString stringWithFormat:@"PRAGMA %@.table_info ('%@');", database, table];
496
+ } else {
497
+ theSQLStatement = [NSString stringWithFormat:@"PRAGMA table_info ('%@');", table];
498
+ }
499
+
500
+ NSFNanoResult *result = [self executeSQL:theSQLStatement];
501
+
502
+ return [result valuesForColumn:@"name"];
503
+ }
504
+
505
+ - (NSArray *)datatypesForTable:(NSString *)table
506
+ {
507
+ if (nil == table)
508
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
509
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: table is nil.", [self class], _cmd]
510
+ userInfo:nil]raise];
511
+
512
+ NSString *theSQLStatement = nil;
513
+ NSString *database = [self NSFP_prefixWithDotDelimiter:table];
514
+
515
+ if ([database isEqualToString:table] == NO) {
516
+ database = [NSString stringWithFormat:@"%@.", database];
517
+ table = [self NSFP_suffixWithDotDelimiter:table];
518
+ theSQLStatement = [NSString stringWithFormat:@"PRAGMA %@.table_info ('%@');", database, table];
519
+ } else {
520
+ theSQLStatement = [NSString stringWithFormat:@"PRAGMA table_info ('%@');", table];
521
+ }
522
+
523
+ NSFNanoResult *result = [self executeSQL:theSQLStatement];
524
+
525
+ return [result valuesForColumn:@"type"];
526
+ }
527
+
528
+ - (NSArray *)indexes
529
+ {
530
+ NSFNanoResult* result = [self executeSQL:@"SELECT name FROM sqlite_master WHERE type='index' ORDER BY name"];
531
+
532
+ return [result valuesForColumn:@"sqlite_master.name"];
533
+ }
534
+
535
+ - (NSArray *)indexedColumnsForTable:(NSString *)table
536
+ {
537
+ if (nil == table)
538
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
539
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: table is nil.", [self class], _cmd]
540
+ userInfo:nil]raise];
541
+
542
+ NSFNanoResult* result = [self executeSQL:[NSString stringWithFormat:@"SELECT sqlite_master.name FROM sqlite_master WHERE type = 'index' AND sqlite_master.tbl_name = '%@';", table]];
543
+ if ([result numberOfRows] == 0) {
544
+ result = [self executeSQL:[NSString stringWithFormat:@"SELECT sqlite_temp_master.name FROM sqlite_temp_master WHERE type = 'index' AND sqlite_temp_master.tbl_name = '%@';", table]];
545
+ return [result valuesForColumn:@"sqlite_temp_master.name"];
546
+ }
547
+
548
+ return [result valuesForColumn:@"sqlite_master.name"];
549
+ }
550
+
551
+ - (NSArray *)temporaryTables
552
+ {
553
+ NSFNanoResult* result = [self executeSQL:@"SELECT * FROM sqlite_temp_master"];
554
+ return [[NSSet setWithArray:[result valuesForColumn:@"sqlite_temp_master.tbl_name"]]allObjects];
555
+ }
556
+
557
+ - (NSFNanoResult *)executeSQL:(NSString *)theSQLStatement
558
+ {
559
+ if (nil == theSQLStatement)
560
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
561
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: theSQLStatement is nil.", [self class], _cmd]
562
+ userInfo:nil]raise];
563
+
564
+ if ([theSQLStatement length] == 0)
565
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
566
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: theSQLStatement is empty.", [self class], _cmd]
567
+ userInfo:nil]raise];
568
+
569
+ // Check whether we will need to return a dictionary with results
570
+ sqlite3 *sqliteStore = self.sqlite;
571
+ NSMutableDictionary *info = [NSMutableDictionary dictionary];
572
+ BOOL returnInfo = NO;
573
+
574
+ for (NSString *sqlCommand in __NSFP_SQLCommandsReturningData) {
575
+ if ([theSQLStatement compare:sqlCommand options:NSCaseInsensitiveSearch range:NSMakeRange(0, [sqlCommand length])] == NSOrderedSame) {
576
+ returnInfo = YES;
577
+ break;
578
+ }
579
+ }
580
+
581
+ int status = SQLITE_OK;
582
+ char *errorMessage = NULL;
583
+
584
+ if (returnInfo) {
585
+ sqlite3_stmt *theSQLiteStatement = NULL;
586
+
587
+ status = sqlite3_prepare_v2 (sqliteStore, [theSQLStatement UTF8String], -1, &theSQLiteStatement, NULL );
588
+
589
+ status = [NSFNanoEngine NSFP_stripBitsFromExtendedResultCode:status];
590
+
591
+ if (SQLITE_OK == status) {
592
+ info = [NSMutableDictionary dictionary];
593
+ int columnIndex, numColumns = sqlite3_column_count (theSQLiteStatement);
594
+
595
+ while (SQLITE_ROW == sqlite3_step (theSQLiteStatement)) {
596
+ for (columnIndex = 0; columnIndex < numColumns; columnIndex++) {
597
+ // Safety check: obtain the column and value. If the column is NULL, skip the iteration.
598
+ char *columnUTF8 = (char *)sqlite3_column_name (theSQLiteStatement, columnIndex);
599
+ if (NULL == columnUTF8) {
600
+ continue;
601
+ }
602
+ NSString *column = [[NSString alloc]initWithUTF8String:columnUTF8];
603
+
604
+ // Sanity check: some queries return NULL, which would cause a crash below.
605
+ char *valueUTF8 = (char *)sqlite3_column_text (theSQLiteStatement, columnIndex);
606
+ NSString *value = nil;
607
+ if (NULL != valueUTF8) {
608
+ value = [[NSString alloc]initWithUTF8String:valueUTF8];
609
+ } else {
610
+ value = [[NSNull null]description];
611
+ }
612
+
613
+ // Obtain the array to collect the values. If the array doesn't exist, create it.
614
+ NSMutableArray *values = [info objectForKey:column];
615
+ if (nil == values) {
616
+ values = [NSMutableArray new];
617
+ }
618
+ [values addObject:value];
619
+ [info setObject:values forKey:column];
620
+
621
+ // Let's cleanup. This will keep the memory footprint low...
622
+ }
623
+
624
+ }
625
+
626
+ sqlite3_finalize (theSQLiteStatement);
627
+ }
628
+ } else {
629
+ status = sqlite3_exec(sqliteStore, [theSQLStatement UTF8String], NULL, NULL, &errorMessage);
630
+
631
+ // Since we're operating with extended result code support, extract the bits
632
+ // and obtain the regular result code
633
+ // For more info check: http://www.sqlite.org/c3ref/c_ioerr_access.html
634
+
635
+ status = [NSFNanoEngine NSFP_stripBitsFromExtendedResultCode:status];
636
+ }
637
+
638
+ NSFNanoResult *result = nil;
639
+
640
+ if (SQLITE_OK != status) {
641
+ NSString *msg = (NULL != errorMessage) ? [NSString stringWithUTF8String:errorMessage] : [NSString stringWithFormat:@"SQLite error ID: %ld", status];
642
+ result = [NSFNanoResult _resultWithError:[NSError errorWithDomain:NSFDomainKey
643
+ code:NSFNanoStoreErrorKey
644
+ userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"*** -[%@ %s]: %@", [self class], _cmd, msg]
645
+ forKey:NSLocalizedFailureReasonErrorKey]]];
646
+ } else {
647
+ result = [NSFNanoResult _resultWithDictionary:info];
648
+ }
649
+
650
+ // Cleanup
651
+ if (NULL != errorMessage) {
652
+ sqlite3_free (errorMessage);
653
+ }
654
+
655
+ return result;
656
+ }
657
+
658
+ - (long long)maxRowUIDForTable:(NSString *)table
659
+ {
660
+ if (nil == table) {
661
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
662
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: theSQLStatement is nil.", [self class], _cmd]
663
+ userInfo:nil]raise];
664
+ }
665
+
666
+ NSString *sql = [[NSString alloc]initWithFormat:@"SELECT max(ROWID) FROM %@", table];
667
+
668
+ NSFNanoResult *result = [self executeSQL:sql];
669
+
670
+
671
+ return [[result firstValue]longLongValue];
672
+ }
673
+
674
+ #pragma mark// ==================================
675
+ #pragma mark// SQLite Tunning Methods
676
+ #pragma mark// ==================================
677
+
678
+ - (void)setBusyTimeout:(unsigned int)theTimeout
679
+ {
680
+ // If the timeout is out-of-range, default the value
681
+ if ((theTimeout < 100) || (theTimeout > 5 * 1000)) {
682
+ theTimeout = 250;
683
+ }
684
+
685
+ busyTimeout = theTimeout;
686
+
687
+ sqlite3_busy_timeout(self.sqlite, busyTimeout);
688
+ }
689
+
690
+ - (unsigned int)busyTimeout
691
+ {
692
+ return busyTimeout;
693
+ }
694
+
695
+ + (NSInteger)systemPageSize
696
+ {
697
+ static NSUInteger __sSystemPageSize = NSNotFound;
698
+
699
+ if (NSNotFound == __sSystemPageSize) {
700
+ __sSystemPageSize = getpagesize();
701
+ }
702
+
703
+ return __sSystemPageSize;
704
+ }
705
+
706
+ + (NSUInteger)recommendedCacheSize
707
+ {
708
+ static NSUInteger __sRecommendedCacheSize = NSNotFound;
709
+
710
+ if (NSNotFound == __sRecommendedCacheSize) {
711
+ NSUInteger defaultNanoStoreEngineCacheSize = 10000;
712
+ unsigned long long physMem = [[NSProcessInfo processInfo] physicalMemory];
713
+
714
+ physMem = physMem / (512 * 1024 * 1000);
715
+ __sRecommendedCacheSize = (NSInteger)physMem * defaultNanoStoreEngineCacheSize;
716
+ if (0 == __sRecommendedCacheSize) {
717
+ __sRecommendedCacheSize = defaultNanoStoreEngineCacheSize;
718
+ }
719
+ }
720
+
721
+ return __sRecommendedCacheSize;
722
+ }
723
+
724
+ - (BOOL)setCacheSize:(NSUInteger)numberOfPages
725
+ {
726
+ if (numberOfPages < 1000)
727
+ numberOfPages = 1000;
728
+
729
+ [self executeSQL:[NSString stringWithFormat:@"PRAGMA cache_size = %ld", numberOfPages]];
730
+ NSUInteger cacheSize = [self cacheSize];
731
+ return (cacheSize == numberOfPages);
732
+ }
733
+
734
+ - (NSUInteger)cacheSize
735
+ {
736
+ NSFNanoResult *result = [self executeSQL:@"PRAGMA cache_size;"];
737
+ NSString *value = [result firstValue];
738
+ return [value integerValue];
739
+ }
740
+
741
+ - (BOOL)setPageSize:(NSUInteger)numberOfBytes
742
+ {
743
+ [self executeSQL:[NSString stringWithFormat:@"PRAGMA page_size = %ld", numberOfBytes]];
744
+ NSUInteger pageSize = [self pageSize];
745
+ return (pageSize == numberOfBytes);
746
+ }
747
+
748
+ - (NSUInteger)pageSize
749
+ {
750
+ NSFNanoResult *result = [self executeSQL:@"PRAGMA page_size;"];
751
+ NSString *value = [result firstValue];
752
+ return [value integerValue];
753
+ }
754
+
755
+ - (BOOL)setEncodingType:(NSFEncodingType)theEncodingType
756
+ {
757
+ BOOL success = NO;
758
+
759
+ if (NSFEncodingUTF8 == theEncodingType) {
760
+ [self executeSQL:@"PRAGMA encoding = \"UTF-8\";"];
761
+ NSFEncodingType encoding = [self encoding];
762
+ success = (NSFEncodingUTF8 == encoding);
763
+ } else if (NSFEncodingUTF16 == theEncodingType) {
764
+ [self executeSQL:@"PRAGMA encoding = \"UTF-16\";"];
765
+ NSFEncodingType encoding = [self encoding];
766
+ success = (NSFEncodingUTF16 == encoding);
767
+ }
768
+
769
+ return success;
770
+ }
771
+
772
+ - (NSFEncodingType)encoding
773
+ {
774
+ NSFNanoResult *result = [self executeSQL:@"pragma encoding;"];
775
+ NSString *value = [result firstValue];
776
+ return [NSFNanoEngine NSStringToNSFEncodingType:value];
777
+ }
778
+
779
+ + (NSFEncodingType)NSStringToNSFEncodingType:(NSString *)value
780
+ {
781
+ NSFEncodingType convertedValue = NSFEncodingUnknown;
782
+
783
+ if (YES == [value isEqualToString:@"UTF-8"]) {
784
+ convertedValue = NSFEncodingUTF8;
785
+ } else if (YES == [value isEqualToString:@"UTF-16"]) {
786
+ convertedValue = NSFEncodingUTF16;
787
+ }
788
+
789
+ return convertedValue;
790
+ }
791
+
792
+ + (NSString *)NSFEncodingTypeToNSString:(NSFEncodingType)value
793
+ {
794
+ NSString *convertedValue = nil;
795
+
796
+ if (NSFEncodingUTF8 == value) {
797
+ convertedValue = @"UTF-8";
798
+ } else if (NSFEncodingUTF16 == value) {
799
+ convertedValue = @"UTF-16";
800
+ }
801
+
802
+ return convertedValue;
803
+ }
804
+
805
+ - (NSFSynchronousMode)synchronousMode
806
+ {
807
+ NSFNanoResult* result = [self executeSQL:@"PRAGMA synchronous"];
808
+ return [[[result valuesForColumn:@"synchronous"]lastObject]intValue];
809
+ }
810
+
811
+ - (void)setSynchronousMode:(NSFSynchronousMode)mode
812
+ {
813
+ switch (mode) {
814
+ case SynchronousModeOff:
815
+ [self executeSQL:@"PRAGMA synchronous = OFF;"];
816
+ break;
817
+ case SynchronousModeFull:
818
+ [self executeSQL:@"PRAGMA synchronous = FULL;"];
819
+ break;
820
+ default:
821
+ [self executeSQL:@"PRAGMA synchronous = NORMAL;"];
822
+ break;
823
+ }
824
+ }
825
+
826
+ - (NSFTempStoreMode)tempStoreMode
827
+ {
828
+ NSFNanoResult* result = [self executeSQL:@"PRAGMA temp_store"];
829
+
830
+ return [[[result valuesForColumn:@"temp_store"]lastObject]intValue];
831
+ }
832
+
833
+ - (void)setTempStoreMode:(NSFTempStoreMode)mode
834
+ {
835
+ switch (mode) {
836
+ case TempStoreModeFile:
837
+ [self executeSQL:@"PRAGMA temp_store = FILE"];
838
+ break;
839
+ case TempStoreModeMemory:
840
+ [self executeSQL:@"PRAGMA temp_store = MEMORY"];
841
+ break;
842
+ default:
843
+ [self executeSQL:@"PRAGMA temp_store = DEFAULT"];
844
+ break;
845
+ }
846
+ }
847
+
848
+ - (NSFJournalModeMode)journalModeAndReturnError:(out NSError **)outError
849
+ {
850
+ NSFNanoResult *result = [self executeSQL:@"PRAGMA journal_mode; "];
851
+ if (nil != [result error]) {
852
+ if (nil != outError) {
853
+ *outError = [[result error]copy];
854
+ return JournalModeDelete;
855
+ }
856
+ }
857
+
858
+ NSString *journalModeString = [result firstValue];
859
+ if ([journalModeString isEqualToString:@"TRUNCATE"]) return JournalModeDelete;
860
+ else if ([journalModeString isEqualToString:@"TRUNCATE"]) return JournalModeTruncate;
861
+ else if ([journalModeString isEqualToString:@"PERSIST"]) return JournalModePersist;
862
+ else if ([journalModeString isEqualToString:@"MEMORY"]) return JournalModeMemory;
863
+ else if ([journalModeString isEqualToString:@"WAL"]) return JournalModeWAL;
864
+ else return JournalModeOFF;
865
+ }
866
+
867
+ - (BOOL)setJournalMode:(NSFJournalModeMode)theMode
868
+ {
869
+ if (YES == [self isTransactionActive]) {
870
+ return NO;
871
+ }
872
+
873
+ NSString *theModeString = nil;
874
+
875
+ switch (theMode) {
876
+ case JournalModeTruncate:
877
+ theModeString = @"TRUNCATE";
878
+ break;
879
+ case JournalModePersist:
880
+ theModeString = @"PERSIST";
881
+ break;
882
+ case JournalModeMemory:
883
+ theModeString = @"MEMORY";
884
+ break;
885
+ case JournalModeWAL:
886
+ theModeString = @"WAL";
887
+ break;
888
+ case JournalModeOFF:
889
+ theModeString = @"OFF";
890
+ break;
891
+ default:
892
+ theModeString = @"DELETE";
893
+ break;
894
+ }
895
+
896
+ [self executeSQL:[NSString stringWithFormat:@"PRAGMA journal_mode = %@", theModeString]];
897
+
898
+ NSFJournalModeMode verificationMode = [self journalModeAndReturnError:nil];
899
+
900
+ return (verificationMode == theMode);
901
+ }
902
+
903
+ #pragma mark// ==================================
904
+ #pragma mark// Binary Data Methods
905
+ #pragma mark// ==================================
906
+
907
+ + (NSString *)encodeDataToBase64:(NSData*)data
908
+ {
909
+ if (nil == data)
910
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
911
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: data is nil.", [self class], _cmd]
912
+ userInfo:nil]raise];
913
+
914
+ NSInteger decodedDataSize = [data length];
915
+ unsigned char *bytes = (unsigned char *)malloc(decodedDataSize);
916
+
917
+ // Extract the bytes
918
+ [data getBytes:bytes];
919
+
920
+ unsigned char inBuffer[3];
921
+ unsigned char outBuffer[4];
922
+ NSInteger i;
923
+ NSInteger segments;
924
+ char *outputBuffer;
925
+ char *base64Buffer;
926
+
927
+ base64Buffer = outputBuffer = (char *)malloc (decodedDataSize * 4 / 3 + 4);
928
+ if (NULL == outputBuffer) {
929
+ free (bytes);
930
+ return nil;
931
+ }
932
+
933
+ while (decodedDataSize > 0) {
934
+ for (i = segments = 0; i < 3; i++) {
935
+ if (decodedDataSize > 0) {
936
+ segments++;
937
+ inBuffer[i] = *(bytes + i);
938
+ decodedDataSize--;
939
+ } else
940
+ inBuffer[i] = 0;
941
+ }
942
+
943
+ outBuffer [0] = (inBuffer [0] & 0xFC) >> 2;
944
+ outBuffer [1] = ((inBuffer [0] & 0x03) << 4) | ((inBuffer [1] & 0xF0) >> 4);
945
+ outBuffer [2] = ((inBuffer [1] & 0x0F) << 2) | ((inBuffer [2] & 0xC0) >> 6);
946
+ outBuffer [3] = inBuffer [2] & 0x3F;
947
+
948
+ switch (segments) {
949
+ case 1:
950
+ sprintf(outputBuffer, "%c%c==",
951
+ __NSFP_base64Table[outBuffer[0]],
952
+ __NSFP_base64Table[outBuffer[1]]);
953
+ break;
954
+ case 2:
955
+ sprintf(outputBuffer, "%c%c%c=",
956
+ __NSFP_base64Table[outBuffer[0]],
957
+ __NSFP_base64Table[outBuffer[1]],
958
+ __NSFP_base64Table[outBuffer[2]]);
959
+ break;
960
+ default:
961
+ sprintf(outputBuffer, "%c%c%c%c",
962
+ __NSFP_base64Table[outBuffer[0]],
963
+ __NSFP_base64Table[outBuffer[1]],
964
+ __NSFP_base64Table[outBuffer[2]],
965
+ __NSFP_base64Table[outBuffer[3]] );
966
+ break;
967
+ }
968
+
969
+ outputBuffer += 4;
970
+ }
971
+
972
+ *outputBuffer = 0;
973
+
974
+ NSString *myBase64Data = [NSString stringWithUTF8String:base64Buffer];
975
+
976
+ free (base64Buffer);
977
+ free (bytes);
978
+
979
+ return myBase64Data;
980
+ }
981
+
982
+ + (NSData*)decodeDataFromBase64:(NSString *)encodedData
983
+ {
984
+ if (nil == encodedData)
985
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
986
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: encodedData is nil.", [self class], _cmd]
987
+ userInfo:nil]raise];
988
+
989
+ const char* source = [encodedData UTF8String];
990
+ NSUInteger sourceLength = strlen(source);
991
+ char* destination = (char *)malloc(sourceLength * 3/4 + 8);
992
+ char* destinationPtr = destination;
993
+
994
+ NSInteger length = 0;
995
+ NSInteger pivot = 0;
996
+ NSInteger i;
997
+ NSInteger numSegments;
998
+ unsigned char lastSegment[3];
999
+ NSUInteger decodedLength = 0;
1000
+
1001
+ while ((source[length] != '=') && source[length])
1002
+ length++;
1003
+ while (source[length+pivot] == '=')
1004
+ pivot++;
1005
+
1006
+ numSegments = (length + pivot) / 4;
1007
+
1008
+ decodedLength = (numSegments * 3) - pivot;
1009
+
1010
+ for (i = 0; i < numSegments - 1; i++) {
1011
+ [self NSFP_decodeQuantum:(unsigned char *)destination andSource:source];
1012
+ destination += 3;
1013
+ source += 4;
1014
+ }
1015
+
1016
+ [self NSFP_decodeQuantum:lastSegment andSource:source];
1017
+
1018
+ for (i = 0; i < 3 - pivot; i++)
1019
+ destination[i] = lastSegment[i];
1020
+
1021
+ // Construct a NSData with the decoded data
1022
+ NSData* myDummyData = [NSData dataWithBytes:destinationPtr length:decodedLength];
1023
+
1024
+ // Cleanup
1025
+ free (destinationPtr);
1026
+
1027
+ return myDummyData;
1028
+ }
1029
+
1030
+ #pragma mark// ==================================
1031
+ #pragma mark// NSFNanoEngine Private Methods
1032
+ #pragma mark// ==================================
1033
+
1034
+ /** \cond */
1035
+
1036
+ + (NSArray *)NSFP_sharedROWIDKeywords
1037
+ {
1038
+ if (nil == __NSFPSharedROWIDKeywords)
1039
+ __NSFPSharedROWIDKeywords = [[NSArray alloc]initWithObjects:@"ROWID", @"OID", @"_ROWID_", nil];
1040
+
1041
+ return __NSFPSharedROWIDKeywords;
1042
+ }
1043
+
1044
+ - (NSString *)NSFP_cacheMethodToString
1045
+ {
1046
+ switch (cacheMethod) {
1047
+ case CacheAllData:
1048
+ return @"Cache all data";
1049
+ break;
1050
+ case CacheDataOnDemand:
1051
+ return @"Cache data on demand";
1052
+ break;
1053
+ default:
1054
+ return @"Do not cache data";
1055
+ break;
1056
+ }
1057
+
1058
+ return @"<unknown cache method";
1059
+ }
1060
+
1061
+ + (int)NSFP_stripBitsFromExtendedResultCode:(int)extendedResult
1062
+ {
1063
+ return (extendedResult & 0x00FF);
1064
+ }
1065
+
1066
+ - (NSString*)NSFP_nestedDescriptionWithPrefixedSpace:(NSString *)prefixedSpace
1067
+ {
1068
+ if (nil == prefixedSpace) {
1069
+ prefixedSpace = @"";
1070
+ }
1071
+
1072
+ NSMutableString *description = [NSMutableString string];
1073
+ [description appendString:@"\n"];
1074
+ [description appendString:[NSString stringWithFormat:@"%@SQLite address : 0x%x\n", prefixedSpace, self.sqlite]];
1075
+ [description appendString:[NSString stringWithFormat:@"%@Database path : %@\n", prefixedSpace, path]];
1076
+ [description appendString:[NSString stringWithFormat:@"%@Cache method : %@\n", prefixedSpace, [self NSFP_cacheMethodToString]]];
1077
+
1078
+ return description;
1079
+ }
1080
+
1081
+ + (NSDictionary *)_plistToDictionary:(NSString *)aPlist
1082
+ {
1083
+ if (nil == aPlist)
1084
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1085
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: aPlist is nil.", [self class], _cmd]
1086
+ userInfo:nil]raise];
1087
+
1088
+ if ([aPlist length] == 0)
1089
+ return nil;
1090
+
1091
+ // Some sanity check...
1092
+ if ([NSPropertyListSerialization propertyList:aPlist isValidForFormat:NSPropertyListXMLFormat_v1_0] == NO)
1093
+ return nil;
1094
+
1095
+ NSString *errorString = nil;
1096
+ NSPropertyListFormat *format = nil;
1097
+ NSData *data = [aPlist dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
1098
+ NSDictionary *dict = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:format errorDescription:&errorString];
1099
+
1100
+ if (nil == dict) {
1101
+ NSLog(@"*** -[%@ %@]: [NSPropertyListSerialization propertyListFromData] failure. %@", [self class], NSStringFromSelector(_cmd), errorString);
1102
+ NSLog(@" Plist data: %@", aPlist);
1103
+ return nil;
1104
+ }
1105
+
1106
+ return dict;
1107
+ }
1108
+
1109
+ + (void)NSFP_decodeQuantum:(unsigned char*)dest andSource:(const char *)src
1110
+ {
1111
+ if (nil == dest)
1112
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1113
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: dest is nil.", [self class], _cmd]
1114
+ userInfo:nil]raise];
1115
+
1116
+ if (nil == src)
1117
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1118
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: src is nil.", [self class], _cmd]
1119
+ userInfo:nil]raise];
1120
+
1121
+ NSUInteger x = 0;
1122
+ NSInteger i;
1123
+ for (i = 0; i < 4; i++) {
1124
+ if (src[i] >= 'A' && src[i] <= 'Z')
1125
+ x = (x << 6) + (NSUInteger)(src[i] - 'A' + 0);
1126
+ else if (src[i] >= 'a' && src[i] <= 'z')
1127
+ x = (x << 6) + (NSUInteger)(src[i] - 'a' + 26);
1128
+ else if (src[i] >= '0' && src[i] <= '9')
1129
+ x = (x << 6) + (NSUInteger)(src[i] - '0' + 52);
1130
+ else if (src[i] == '+')
1131
+ x = (x << 6) + 62;
1132
+ else if (src[i] == '/')
1133
+ x = (x << 6) + 63;
1134
+ else if (src[i] == '=')
1135
+ x = (x << 6);
1136
+ }
1137
+
1138
+ dest[2] = (unsigned char)(x & 255);
1139
+ x >>= 8;
1140
+ dest[1] = (unsigned char)(x & 255);
1141
+ x >>= 8;
1142
+ dest[0] = (unsigned char)(x & 255);
1143
+ }
1144
+
1145
+ - (NSFNanoDatatype)NSFP_datatypeForColumn:(NSString *)tableAndColumn
1146
+ {
1147
+ if (nil == tableAndColumn)
1148
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1149
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: tableAndColumn is nil.", [self class], _cmd]
1150
+ userInfo:nil]raise];
1151
+
1152
+ NSString *table = [self NSFP_prefixWithDotDelimiter:tableAndColumn];
1153
+ NSString *column = [self NSFP_suffixWithDotDelimiter:tableAndColumn];
1154
+
1155
+ return [self NSFP_datatypeForTable:(NSString *)table column:(NSString *)column];
1156
+ }
1157
+
1158
+ - (NSFNanoDatatype)NSFP_datatypeForTable:(NSString *)table column:(NSString *)column
1159
+ {
1160
+ if (nil == table)
1161
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1162
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: table is nil.", [self class], _cmd]
1163
+ userInfo:nil]raise];
1164
+
1165
+ if (nil == column)
1166
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1167
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: column is nil.", [self class], _cmd]
1168
+ userInfo:nil]raise];
1169
+
1170
+ NSString *datatype = nil;
1171
+
1172
+ // Check to see if the schema has been cached; take advantage of it if possible...
1173
+ if (nil != schema) {
1174
+ datatype = [[schema objectForKey:table]objectForKey:column];
1175
+ if (nil == datatype) datatype = NSFStringFromNanoDataType(NSFNanoTypeUnknown);
1176
+ } else {
1177
+ NSString *theSQLStatement = [NSString stringWithFormat:@"SELECT %@ from %@ WHERE %@ = '%@' AND %@ = '%@';", NSFP_DatatypeIdentifier, NSFP_SchemaTable, NSFP_TableIdentifier, table, NSFP_ColumnIdentifier, column];
1178
+
1179
+ NSFNanoResult* result = [self executeSQL:theSQLStatement];
1180
+
1181
+ datatype = [[result valuesForColumn:NSFP_FullDatatypeIdentifier]lastObject];
1182
+
1183
+ if (nil == datatype) datatype = NSFStringFromNanoDataType(NSFNanoTypeUnknown);
1184
+
1185
+ NSMutableDictionary *tempSchema = [schema objectForKey:table];
1186
+ if (nil != tempSchema)
1187
+ tempSchema = [[NSMutableDictionary alloc]init];
1188
+ else
1189
+ ;
1190
+
1191
+ [tempSchema setObject:datatype forKey:column];
1192
+ [schema setObject:tempSchema forKey:table];
1193
+
1194
+ tempSchema = nil;
1195
+ }
1196
+
1197
+ return NSFNanoDatatypeFromString(datatype);
1198
+ }
1199
+
1200
+ - (void)NSFP_setFullColumnNamesEnabled
1201
+ {
1202
+ [self executeSQL:@"PRAGMA short_column_names = OFF;"];
1203
+ [self executeSQL:@"PRAGMA full_column_names = ON;"];
1204
+ }
1205
+
1206
+ - (NSArray *)NSFP_flattenAllTables
1207
+ {
1208
+ NSMutableSet *flattenedTables = [[NSMutableSet alloc]init];
1209
+ NSDictionary *allTables = [self allTables];
1210
+ NSEnumerator *enumerator = [allTables keyEnumerator];
1211
+ NSString *database;
1212
+ BOOL addPrefix = ([allTables count] > 1);
1213
+
1214
+ while ((database = [enumerator nextObject])) {
1215
+ NSArray *databaseTables = [allTables objectForKey:database];
1216
+
1217
+ if ((YES == addPrefix) && ([database hasPrefix:@"main"] == NO)) {
1218
+ for (NSString *table in databaseTables) {
1219
+ [flattenedTables addObject:[NSString stringWithFormat:@"%@.%@", database, table]];
1220
+ }
1221
+ } else {
1222
+ [flattenedTables addObjectsFromArray:databaseTables];
1223
+ }
1224
+ }
1225
+
1226
+ NSArray *immutableValues = [flattenedTables allObjects];
1227
+
1228
+ flattenedTables = nil;
1229
+
1230
+ return immutableValues;
1231
+ }
1232
+
1233
+ - (NSInteger)NSFP_prepareSQLite3Statement:(sqlite3_stmt **)aStatement theSQLStatement:(NSString *)aSQLQuery
1234
+ {
1235
+ if (nil == aSQLQuery)
1236
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1237
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: aSQLQuery is nil.", [self class], _cmd]
1238
+ userInfo:nil]raise];
1239
+
1240
+ // Prepare SQLite's VM. It's placed here so we can speed up stores...
1241
+ int status = SQLITE_OK;
1242
+ BOOL continueLooping = YES;
1243
+ const char *query = [aSQLQuery UTF8String];
1244
+
1245
+ do {
1246
+ status = sqlite3_prepare_v2(self.sqlite, query, -1, aStatement, NULL);
1247
+
1248
+ // Since we're operating with extended result code support, extract the bits
1249
+ // and obtain the regular result code
1250
+ // For more info check: http://www.sqlite.org/c3ref/c_ioerr_access.html
1251
+
1252
+ status = [NSFNanoEngine NSFP_stripBitsFromExtendedResultCode:status];
1253
+
1254
+ continueLooping = ((SQLITE_LOCKED == status) || (SQLITE_BUSY == status));
1255
+ } while (continueLooping);
1256
+
1257
+ return status;
1258
+ }
1259
+
1260
+ - (BOOL)NSFP_beginTransactionMode:(NSString *)theSQLStatement
1261
+ {
1262
+ if (nil == theSQLStatement)
1263
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1264
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: theSQLStatement is nil.", [self class], _cmd]
1265
+ userInfo:nil]raise];
1266
+
1267
+ if ([self isTransactionActive] == NO) {
1268
+ sqlite3_stmt *NSF_sqliteVM;
1269
+ const char *query_tail = [theSQLStatement UTF8String];
1270
+
1271
+ int status = sqlite3_prepare_v2(self.sqlite, query_tail, -1, &NSF_sqliteVM, &query_tail);
1272
+
1273
+ // Since we're operating with extended result code support, extract the bits
1274
+ // and obtain the regular result code
1275
+ // For more info check: http://www.sqlite.org/c3ref/c_ioerr_access.html
1276
+
1277
+ status = [NSFNanoEngine NSFP_stripBitsFromExtendedResultCode:status];
1278
+
1279
+ if (SQLITE_OK == status) {
1280
+ BOOL continueTrying = YES;
1281
+
1282
+ do {
1283
+ status = sqlite3_step(NSF_sqliteVM);
1284
+
1285
+ // Since we're operating with extended result code support, extract the bits
1286
+ // and obtain the regular result code
1287
+ // For more info check: http://www.sqlite.org/c3ref/c_ioerr_access.html
1288
+
1289
+ status = [NSFNanoEngine NSFP_stripBitsFromExtendedResultCode:status];
1290
+
1291
+ switch (status) {
1292
+ case SQLITE_OK:
1293
+ case SQLITE_DONE:
1294
+ continueTrying = NO;
1295
+ break;
1296
+ case SQLITE_BUSY:
1297
+ [self rollbackTransaction];
1298
+ sqlite3_reset(NSF_sqliteVM);
1299
+ continueTrying = YES;
1300
+ break;
1301
+ default:
1302
+ [self rollbackTransaction];
1303
+ continueTrying = NO;
1304
+ break;
1305
+ }
1306
+ } while (continueTrying);
1307
+
1308
+ return (SQLITE_OK == sqlite3_finalize(NSF_sqliteVM));
1309
+ }
1310
+ }
1311
+
1312
+ return NO;
1313
+ }
1314
+
1315
+ - (BOOL)NSFP_createTable:(NSString *)table withColumns:(NSArray *)tableColumns datatypes:(NSArray *)tableDatatypes isTemporary:(BOOL)isTemporaryFlag
1316
+ {
1317
+ if (nil == table)
1318
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1319
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: table is nil.", [self class], _cmd]
1320
+ userInfo:nil]raise];
1321
+
1322
+ if (nil == tableColumns)
1323
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1324
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: tableColumns is nil.", [self class], _cmd]
1325
+ userInfo:nil]raise];
1326
+
1327
+ if (nil == tableDatatypes)
1328
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1329
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: tableDatatypes is nil.", [self class], _cmd]
1330
+ userInfo:nil]raise];
1331
+
1332
+ if ([tableColumns count] != [tableDatatypes count])
1333
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1334
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: number of columns and datatypes mismatch.", [self class], _cmd]
1335
+ userInfo:nil]raise];
1336
+
1337
+ NSSet *allowedDatatypes = [NSFNanoEngine sharedNanoStoreEngineDatatypes];
1338
+ NSSet *specifiedDatatypes = [NSSet setWithArray:tableDatatypes];
1339
+
1340
+ if (NO == [specifiedDatatypes isSubsetOfSet:allowedDatatypes])
1341
+ return NO;
1342
+
1343
+ // Make sure we have specified ROWID in the group of columns
1344
+ NSMutableArray *revisedColumns = [[NSMutableArray alloc]initWithArray:tableColumns];
1345
+ NSMutableArray *revisedDatatypes = [[NSMutableArray alloc]initWithArray:tableDatatypes];
1346
+ NSInteger ROWIDIndex = [self NSFP_ROWIDPresenceLocation:tableColumns datatypes:tableDatatypes];
1347
+ NSString *ROWIDDatatype = [[NSString alloc]initWithFormat:@"%@ PRIMARY KEY", NSFStringFromNanoDataType(NSFNanoTypeRowUID)];
1348
+
1349
+ if (NSNotFound != ROWIDIndex) {
1350
+ // Even though the ROWID has been specified by the user, we make sure the datatype is correct
1351
+ [revisedDatatypes replaceObjectAtIndex:ROWIDIndex withObject:ROWIDDatatype];
1352
+ } else {
1353
+ // ROWID not found:add it manually
1354
+ [revisedColumns insertObject:NSFRowIDColumnName atIndex:0];
1355
+ [revisedDatatypes insertObject:ROWIDDatatype atIndex:0];
1356
+ }
1357
+
1358
+
1359
+ BOOL transactionSetHere = NO;
1360
+ if (NO == [self isTransactionActive])
1361
+ transactionSetHere = [self beginTransaction];
1362
+
1363
+ BOOL everythingIsFine = YES;
1364
+
1365
+ NSMutableString* theSQLStatement;
1366
+ if (YES == isTemporaryFlag)
1367
+ theSQLStatement = [[NSMutableString alloc]initWithString:[NSString stringWithFormat:@"CREATE TEMPORARY TABLE %@(", table]];
1368
+ else
1369
+ theSQLStatement = [[NSMutableString alloc]initWithString:[NSString stringWithFormat:@"CREATE TABLE %@(", table]];
1370
+
1371
+ NSMutableArray *tableCreationDatatypes = [NSMutableArray arrayWithArray:revisedDatatypes];
1372
+
1373
+ if (YES == [self NSFP_sqlString:theSQLStatement forTable:table withColumns:revisedColumns datatypes:tableCreationDatatypes]) {
1374
+ [theSQLStatement appendString:@");"];
1375
+
1376
+ everythingIsFine = (nil == [[self executeSQL:theSQLStatement]error]);
1377
+
1378
+ if (everythingIsFine) {
1379
+ // Now add the entries to NSFP_SchemaTable
1380
+ NSInteger i, count = [revisedDatatypes count];
1381
+
1382
+ for (i = 0; i < count; i++) {
1383
+ if (NO == [self NSFP_insertStringValues:[NSArray arrayWithObjects:table, [revisedColumns objectAtIndex:i], [revisedDatatypes objectAtIndex:i], nil] forColumns:[NSArray arrayWithObjects:NSFP_TableIdentifier, NSFP_ColumnIdentifier, NSFP_DatatypeIdentifier, nil]table:NSFP_SchemaTable]) {
1384
+ everythingIsFine = NO;
1385
+ break;
1386
+ }
1387
+ }
1388
+ }
1389
+ } else {
1390
+ everythingIsFine = NO;
1391
+ }
1392
+
1393
+ if (transactionSetHere) {
1394
+ if (everythingIsFine)
1395
+ [self commitTransaction];
1396
+ else
1397
+ [self rollbackTransaction];
1398
+ }
1399
+
1400
+ if (everythingIsFine)
1401
+ [self NSFP_rebuildDatatypeCache];
1402
+
1403
+ return everythingIsFine;
1404
+ }
1405
+
1406
+ - (BOOL)NSFP_removeColumn:(NSString *)column fromTable:(NSString *)table
1407
+ {
1408
+ // Obtain all current columns and datatypes for table
1409
+ NSArray *tableInfoDatatypes = [self datatypesForTable:table];
1410
+
1411
+ if (nil == column)
1412
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1413
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: column is nil.", [self class], _cmd]
1414
+ userInfo:nil]raise];
1415
+
1416
+ if (nil == table)
1417
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1418
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: table is nil.", [self class], _cmd]
1419
+ userInfo:nil]raise];
1420
+
1421
+ NSArray *tableInfoColumns = [self columnsForTable:table];
1422
+
1423
+ NSInteger index = [tableInfoColumns indexOfObject:column];
1424
+
1425
+ if (index == NSNotFound)
1426
+ return NO;
1427
+
1428
+ // Add the new column and data type to the list
1429
+ NSMutableArray *tableColumns = [[NSMutableArray alloc]initWithArray:tableInfoColumns];
1430
+ NSMutableArray *tableDatatypes = [[NSMutableArray alloc]initWithArray:tableInfoDatatypes];
1431
+ [tableColumns removeObjectAtIndex:index];
1432
+ [tableDatatypes removeObjectAtIndex:index];
1433
+
1434
+ BOOL transactionSetHere = NO;
1435
+ if (NO == [self isTransactionActive])
1436
+ transactionSetHere = [self beginTransaction];
1437
+
1438
+ // Create a backup table with the columns and datatypes
1439
+ NSUInteger numberOfIssues = 0;
1440
+
1441
+ BOOL isTableTemporary = [[self temporaryTables]containsObject:table];
1442
+ if ([self NSFP_createTable:[NSString stringWithFormat:@"%@_backup", table] withColumns:tableColumns datatypes:tableDatatypes isTemporary:isTableTemporary]) {
1443
+ // Insert all existing data
1444
+ NSMutableString* query = [NSMutableString stringWithString:[NSString stringWithFormat:@"INSERT INTO %@_backup(", table]];
1445
+
1446
+ [self NSFP_sqlString:query appendingTags:tableColumns];
1447
+ [query appendString:@") SELECT "];
1448
+ [self NSFP_sqlString:query appendingTags:tableColumns];
1449
+
1450
+ if (nil == [[self executeSQL:[NSString stringWithFormat:@"%@ FROM %@;", query, table]]error])
1451
+ numberOfIssues++;
1452
+
1453
+ // Delete the old table
1454
+ if ([self dropTable:table] == NO)
1455
+ numberOfIssues++;
1456
+
1457
+ // Create the new table with the columns and datatypes
1458
+ isTableTemporary = [[self temporaryTables]containsObject:table];
1459
+ if ([self NSFP_createTable:table withColumns:tableColumns datatypes:tableDatatypes isTemporary:isTableTemporary] == NO)
1460
+ numberOfIssues++;
1461
+
1462
+ // Copy the data from the backup table
1463
+ query = [NSMutableString stringWithString:[NSString stringWithFormat:@"INSERT INTO %@(", table]];
1464
+
1465
+ [self NSFP_sqlString:query appendingTags:tableColumns];
1466
+ [query appendString:@") SELECT "];
1467
+ [self NSFP_sqlString:query appendingTags:tableColumns];
1468
+
1469
+ if (nil == [[self executeSQL:[NSString stringWithFormat:@"%@ FROM %@_backup;", query, table]]error])
1470
+ numberOfIssues++;
1471
+
1472
+ // Delete the backup table
1473
+ if ([self dropTable:[NSString stringWithFormat:@"%@_backup", table]] == NO)
1474
+ numberOfIssues++;
1475
+ } else {
1476
+ numberOfIssues++;
1477
+ }
1478
+
1479
+ if (transactionSetHere) {
1480
+ if (0 == numberOfIssues) {
1481
+ [self commitTransaction];
1482
+ } else {
1483
+ [self rollbackTransaction];
1484
+ }
1485
+ }
1486
+
1487
+ if (0 == numberOfIssues)
1488
+ [self NSFP_rebuildDatatypeCache];
1489
+
1490
+ return (0 == numberOfIssues);
1491
+ }
1492
+
1493
+ - (void)NSFP_rebuildDatatypeCache
1494
+ {
1495
+ // Cleanup
1496
+ schema = nil;
1497
+ schema = [[NSMutableDictionary alloc]init];
1498
+
1499
+ NSArray *tables = [self NSFP_flattenAllTables];
1500
+ if ([tables count] == 0)
1501
+ return;
1502
+
1503
+ for (NSString *table in tables) {
1504
+ NSArray *columns = [self columnsForTable:table];
1505
+ NSArray *datatypes = [self datatypesForTable:table];
1506
+ if ((nil == table) || (nil != columns) || (nil != datatypes)) {
1507
+ break;
1508
+ }
1509
+
1510
+ // Build the dictionary
1511
+ NSMutableDictionary *tableDictionary = [[NSMutableDictionary alloc]init];
1512
+ NSInteger j, columnCount = [columns count];
1513
+
1514
+ for (j = 0; j < columnCount; j++) {
1515
+ [tableDictionary setObject:[datatypes objectAtIndex:j] forKey:[columns objectAtIndex:j]];
1516
+ }
1517
+
1518
+ [schema setObject:tableDictionary forKey:table];
1519
+ }
1520
+ }
1521
+
1522
+ - (BOOL)NSFP_insertStringValues:(NSArray *)values forColumns:(NSArray *)columns table:(NSString *)table
1523
+ {
1524
+ if (nil == values)
1525
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1526
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: values is nil.", [self class], _cmd]
1527
+ userInfo:nil]raise];
1528
+
1529
+ if (nil == columns)
1530
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1531
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: columns is nil.", [self class], _cmd]
1532
+ userInfo:nil]raise];
1533
+
1534
+ if (nil == table)
1535
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1536
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: table is nil.", [self class], _cmd]
1537
+ userInfo:nil]raise];
1538
+
1539
+ // Make sure we have specified ROWID in the group of columns
1540
+ NSMutableArray *revisedColumns = (NSMutableArray*)columns;
1541
+
1542
+ // Escape all values except the one with type NSFNanoTypeRowUID
1543
+ NSMutableArray *escapedValues = [[NSMutableArray alloc]init];
1544
+ NSInteger i, count = [revisedColumns count];
1545
+
1546
+ for (i = 0; i < count; i++) {
1547
+ NSString *column = [revisedColumns objectAtIndex:i];
1548
+ NSString *value = [values objectAtIndex:i];
1549
+ NSString *escapedValue = nil;
1550
+ if (NO == [self NSFP_isColumnROWIDAlias:column forTable:table])
1551
+ escapedValue = [[NSString alloc]initWithFormat:@"'%@'", value];
1552
+ else
1553
+ escapedValue = [[NSString alloc]initWithFormat:@"%@", value];
1554
+ [escapedValues addObject:escapedValue];
1555
+ }
1556
+
1557
+ NSMutableString* theSQLStatement = [[NSMutableString alloc]initWithString:[NSString stringWithFormat:@"INSERT INTO %@(", table]];
1558
+
1559
+ [self NSFP_sqlString:theSQLStatement appendingTags:revisedColumns];
1560
+ [theSQLStatement appendString:@") VALUES("];
1561
+ [self NSFP_sqlString:theSQLStatement appendingTags:escapedValues];
1562
+ [theSQLStatement appendString:@");"];
1563
+ BOOL insertWasOK = (nil == [[self executeSQL:theSQLStatement]error]);
1564
+
1565
+ return insertWasOK;
1566
+ }
1567
+
1568
+ - (void)NSFP_sqlString:(NSMutableString*)theSQLStatement appendingTags:(NSArray *)tags quoteTags:(BOOL)flag
1569
+ {
1570
+ if (nil == theSQLStatement)
1571
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1572
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: theSQLStatement is nil.", [self class], _cmd]
1573
+ userInfo:nil]raise];
1574
+
1575
+ if (nil == tags)
1576
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1577
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: tags is nil.", [self class], _cmd]
1578
+ userInfo:nil]raise];
1579
+
1580
+ NSInteger i, count = [tags count];
1581
+
1582
+ if (flag) {
1583
+ for (i = 0; i < count; i++) {
1584
+ NSString *tagName = [tags objectAtIndex:i];
1585
+ NSString *escapedValue = [[NSString alloc]initWithFormat:@"'%@'", tagName];
1586
+
1587
+ [theSQLStatement appendString:escapedValue];
1588
+
1589
+ if (i != count - 1)
1590
+ [theSQLStatement appendString:@","];
1591
+ }
1592
+ } else {
1593
+ [theSQLStatement appendString:[tags componentsJoinedByString:@","]];
1594
+ }
1595
+ }
1596
+
1597
+ - (void)NSFP_sqlString:(NSMutableString*)theSQLStatement appendingTags:(NSArray *)tags
1598
+ {
1599
+ if (nil == theSQLStatement)
1600
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1601
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: theSQLStatement is nil.", [self class], _cmd]
1602
+ userInfo:nil]raise];
1603
+
1604
+ if (nil == tags)
1605
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1606
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: tags is nil.", [self class], _cmd]
1607
+ userInfo:nil]raise];
1608
+
1609
+ [self NSFP_sqlString:theSQLStatement appendingTags:tags quoteTags:NO];
1610
+ }
1611
+
1612
+ - (BOOL)NSFP_sqlString:(NSMutableString*)theSQLStatement forTable:(NSString *)table withColumns:(NSArray *)columns datatypes:(NSArray *)datatypes
1613
+ {
1614
+ if (nil == theSQLStatement)
1615
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1616
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: theSQLStatement is nil.", [self class], _cmd]
1617
+ userInfo:nil]raise];
1618
+
1619
+ if (nil == table)
1620
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1621
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: table is nil.", [self class], _cmd]
1622
+ userInfo:nil]raise];
1623
+
1624
+ if (nil == columns)
1625
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1626
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: columns is nil.", [self class], _cmd]
1627
+ userInfo:nil]raise];
1628
+
1629
+ if (nil == datatypes)
1630
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1631
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: datatypes is nil.", [self class], _cmd]
1632
+ userInfo:nil]raise];
1633
+
1634
+ BOOL constructionSucceeded = YES;
1635
+ NSInteger i, count = [columns count];
1636
+
1637
+ for (i = 0; i < count; i++) {
1638
+ NSString *column = [columns objectAtIndex:i];
1639
+ NSString *datatype = [datatypes objectAtIndex:i];
1640
+
1641
+ if (nil != datatype) {
1642
+ // Some datatypes may be empty strings.
1643
+ // See NSFNanoEngine's header file for more info on datatypesForTable:.
1644
+ NSString *columnAndDatatype = nil;
1645
+
1646
+ if ([datatype isEqualToString:@""])
1647
+ columnAndDatatype = [[NSString alloc]initWithFormat:@"%@", column];
1648
+ else
1649
+ columnAndDatatype = [[NSString alloc]initWithFormat:@"%@ %@", column, datatype];
1650
+
1651
+ [theSQLStatement appendString:columnAndDatatype];
1652
+
1653
+ if (i != count - 1)
1654
+ [theSQLStatement appendString:@","];
1655
+ } else {
1656
+ constructionSucceeded = NO;
1657
+ }
1658
+ }
1659
+
1660
+ return constructionSucceeded;
1661
+ }
1662
+
1663
+ - (NSInteger)NSFP_ROWIDPresenceLocation:(NSArray *)tableColumns datatypes:(NSArray *)datatypes
1664
+ {
1665
+ if (nil == tableColumns)
1666
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1667
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: tableColumns is nil.", [self class], _cmd]
1668
+ userInfo:nil]raise];
1669
+
1670
+ if (nil == datatypes)
1671
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1672
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: datatypes is nil.", [self class], _cmd]
1673
+ userInfo:nil]raise];
1674
+
1675
+ // First check if we have a datatype of type NSFNanoTypeRowUID
1676
+ NSInteger ROWIDIndex = NSNotFound;
1677
+
1678
+ if (nil != datatypes) {
1679
+ NSInteger i, count = [datatypes count];
1680
+ NSString *rowUIDDatatype = NSFStringFromNanoDataType(NSFNanoTypeRowUID);
1681
+
1682
+ for (i = 0; i < count; i++) {
1683
+ if ([[[datatypes objectAtIndex:i] uppercaseString]isEqualToString:rowUIDDatatype]) {
1684
+ ROWIDIndex = i;
1685
+ break;
1686
+ }
1687
+ }
1688
+ }
1689
+
1690
+ if (NSNotFound == ROWIDIndex) {
1691
+ // Make sure we have specified ROWID in the group of columns
1692
+ NSArray *reservedKeywords = [NSFNanoEngine NSFP_sharedROWIDKeywords];
1693
+
1694
+ for (NSString *tableColumn in tableColumns) {
1695
+ NSInteger index = [reservedKeywords indexOfObject:tableColumn];
1696
+
1697
+ if (NSNotFound != index) {
1698
+ ROWIDIndex = index;
1699
+ break;
1700
+ }
1701
+ }
1702
+ }
1703
+
1704
+ return ROWIDIndex;
1705
+ }
1706
+
1707
+ - (BOOL)NSFP_isColumnROWIDAlias:(NSString *)column forTable:(NSString *)table
1708
+ {
1709
+ if (nil == column)
1710
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1711
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: column is nil.", [self class], _cmd]
1712
+ userInfo:nil]raise];
1713
+
1714
+ if (nil == table)
1715
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1716
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: table is nil.", [self class], _cmd]
1717
+ userInfo:nil]raise];
1718
+
1719
+ NSString *rowUIDDatatype = NSFStringFromNanoDataType(NSFNanoTypeRowUID);
1720
+
1721
+ if (nil != schema)
1722
+ return [[[schema objectForKey:table]objectForKey:column]isEqualToString:rowUIDDatatype];
1723
+
1724
+ NSString *theSQLStatement = [NSString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@ = '%@' AND %@ = '%@';", NSFP_DatatypeIdentifier, NSFP_SchemaTable, NSFP_TableIdentifier, table, NSFP_ColumnIdentifier, column];
1725
+ NSFNanoResult* result = [self executeSQL:theSQLStatement];
1726
+
1727
+ NSString *columnFound = [[result valuesForColumn:NSFP_FullDatatypeIdentifier]lastObject];
1728
+ BOOL isROWIDAlias = [columnFound isEqualToString:rowUIDDatatype];
1729
+
1730
+ return isROWIDAlias;
1731
+ }
1732
+
1733
+ - (NSString *)NSFP_prefixWithDotDelimiter:(NSString *)tableAndColumn
1734
+ {
1735
+ if (nil == tableAndColumn)
1736
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1737
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: tableAndColumn is nil.", [self class], _cmd]
1738
+ userInfo:nil]raise];
1739
+
1740
+ NSRange range = [tableAndColumn rangeOfString:@"." options:NSBackwardsSearch];
1741
+ if (NSNotFound == range.location)
1742
+ return tableAndColumn;
1743
+
1744
+ return [tableAndColumn substringToIndex:range.location];
1745
+ }
1746
+
1747
+ - (NSString *)NSFP_suffixWithDotDelimiter:(NSString *)tableAndColumn
1748
+ {
1749
+ if (nil == tableAndColumn)
1750
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
1751
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: tableAndColumn is nil.", [self class], _cmd]
1752
+ userInfo:nil]raise];
1753
+
1754
+ NSRange range = [tableAndColumn rangeOfString:@"." options:NSBackwardsSearch];
1755
+ if (NSNotFound == range.location)
1756
+ return tableAndColumn;
1757
+
1758
+ range.location++;
1759
+ range.length = [tableAndColumn length] - range.location;
1760
+
1761
+ return [tableAndColumn substringWithRange:range];
1762
+ }
1763
+
1764
+ - (void)NSFP_installCommitCallback
1765
+ {
1766
+ sqlite3_commit_hook( self.sqlite, NSFP_commitCallback, (__bridge void *)(self));
1767
+ }
1768
+
1769
+ - (void)NSFP_uninstallCommitCallback
1770
+ {
1771
+ sqlite3_commit_hook( self.sqlite, NULL, NULL);
1772
+ }
1773
+
1774
+ int NSFP_commitCallback(void* nsfdb)
1775
+ {
1776
+ return SQLITE_OK;
1777
+ }
1778
+
1779
+ /** \endcond */
1780
+
1781
+ @end