appjam 0.1.8.6 → 0.1.8.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/lib/appjam.rb +1 -1
  2. data/lib/appjam/generators/blank.rb +133 -0
  3. data/lib/appjam/generators/help.rb +5 -3
  4. data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/project.pbxproj +855 -0
  5. data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  6. data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/project.xcworkspace/xcuserdata/eiffel.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  7. data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/xcuserdata/eiffel.xcuserdatad/xcschemes/EiffelApplication.xcscheme +96 -0
  8. data/lib/appjam/generators/templates/blank/EiffelApplication.xcodeproj/xcuserdata/eiffel.xcuserdatad/xcschemes/xcschememanagement.plist +27 -0
  9. data/lib/appjam/generators/templates/blank/EiffelApplication/AppDelegate.h.tt +22 -0
  10. data/lib/appjam/generators/templates/blank/EiffelApplication/AppDelegate.m.tt +156 -0
  11. data/lib/appjam/generators/templates/blank/EiffelApplication/EiffelApplication-Info.plist +45 -0
  12. data/lib/appjam/generators/templates/blank/EiffelApplication/EiffelApplication-Prefix.pch.tt +30 -0
  13. data/lib/appjam/generators/templates/blank/EiffelApplication/en.lproj/InfoPlist.strings +2 -0
  14. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFHTTPClient.h +636 -0
  15. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFHTTPClient.m +1359 -0
  16. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFHTTPRequestOperation.h +133 -0
  17. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFHTTPRequestOperation.m +318 -0
  18. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFImageRequestOperation.h +108 -0
  19. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFImageRequestOperation.m +234 -0
  20. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFJSONRequestOperation.h +71 -0
  21. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFJSONRequestOperation.m +142 -0
  22. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFNetworkActivityIndicatorManager.h +75 -0
  23. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFNetworkActivityIndicatorManager.m +145 -0
  24. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFNetworking.h +43 -0
  25. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFPropertyListRequestOperation.h +68 -0
  26. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFPropertyListRequestOperation.m +142 -0
  27. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFURLConnectionOperation.h +379 -0
  28. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFURLConnectionOperation.m +818 -0
  29. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFXMLRequestOperation.h +89 -0
  30. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/AFXMLRequestOperation.m +166 -0
  31. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/UIImageView+AFNetworking.h +78 -0
  32. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/AFNetworking/UIImageView+AFNetworking.m +184 -0
  33. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/JSONKit/JSONKit.h +251 -0
  34. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/JSONKit/JSONKit.m +3067 -0
  35. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabase.h +155 -0
  36. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabase.m +1162 -0
  37. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabaseAdditions.h +37 -0
  38. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabaseAdditions.m +163 -0
  39. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabasePool.h +75 -0
  40. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabasePool.m +244 -0
  41. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabaseQueue.h +38 -0
  42. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMDatabaseQueue.m +176 -0
  43. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMResultSet.h +104 -0
  44. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/frameworks/fmdb/FMResultSet.m +413 -0
  45. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/EGOCache/EGOCache.h +78 -0
  46. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/EGOCache/EGOCache.m +370 -0
  47. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDASLLogger.h +41 -0
  48. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDASLLogger.m +99 -0
  49. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDAbstractDatabaseLogger.h +102 -0
  50. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDAbstractDatabaseLogger.m +727 -0
  51. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDFileLogger.h +334 -0
  52. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDFileLogger.m +1353 -0
  53. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDLog.h +601 -0
  54. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDLog.m +1083 -0
  55. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDTTYLogger.h +167 -0
  56. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/DDTTYLogger.m +1479 -0
  57. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/Extensions/ContextFilterLogFormatter.h +65 -0
  58. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/Extensions/ContextFilterLogFormatter.m +191 -0
  59. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/Extensions/DispatchQueueLogFormatter.h +116 -0
  60. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Lumberjack/Extensions/DispatchQueueLogFormatter.m +251 -0
  61. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/ObjectiveMixin/Mixin.h +33 -0
  62. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/ObjectiveMixin/Mixin.m +122 -0
  63. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/USArrayWrapper.h +72 -0
  64. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/USArrayWrapper.m +305 -0
  65. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/USConstants.h +38 -0
  66. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/USDictionaryWrapper.h +57 -0
  67. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/USDictionaryWrapper.m +188 -0
  68. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/Underscore+Functional.h +89 -0
  69. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/Underscore+Functional.m +261 -0
  70. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/Underscore-Prefix.pch +7 -0
  71. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/Underscore.h +50 -0
  72. data/lib/appjam/generators/templates/blank/EiffelApplication/libs/toolkit/Underscore/Underscore.m +100 -0
  73. data/lib/appjam/generators/templates/blank/EiffelApplication/main.m.tt +18 -0
  74. data/lib/appjam/generators/templates/blank/EiffelApplicationTests/EiffelApplicationTests-Info.plist +22 -0
  75. data/lib/appjam/generators/templates/blank/EiffelApplicationTests/EiffelApplicationTests.h.tt +13 -0
  76. data/lib/appjam/generators/templates/blank/EiffelApplicationTests/EiffelApplicationTests.m.tt +32 -0
  77. data/lib/appjam/generators/templates/blank/EiffelApplicationTests/en.lproj/InfoPlist.strings +2 -0
  78. data/lib/appjam/generators/templates/resources/Default-568h@2x.png +0 -0
  79. data/lib/appjam/generators/templates/resources/Default.png +0 -0
  80. data/lib/appjam/generators/templates/resources/Default@2x.png +0 -0
  81. data/lib/appjam/generators/templates/resources/contents.tt +4 -0
  82. data/lib/appjam/version.rb +1 -1
  83. metadata +462 -326
  84. data/test/helper.rb +0 -132
  85. data/test/test_model_generator.rb +0 -28
  86. data/test/test_project_generator.rb +0 -38
@@ -0,0 +1,155 @@
1
+ #import <Foundation/Foundation.h>
2
+ #import "sqlite3.h"
3
+ #import "FMResultSet.h"
4
+ #import "FMDatabasePool.h"
5
+
6
+
7
+ #if ! __has_feature(objc_arc)
8
+ #define FMDBAutorelease(__v) ([__v autorelease]);
9
+ #define FMDBReturnAutoreleased FMDBAutorelease
10
+
11
+ #define FMDBRetain(__v) ([__v retain]);
12
+ #define FMDBReturnRetained FMDBRetain
13
+
14
+ #define FMDBRelease(__v) ([__v release]);
15
+
16
+ #define FMDBDispatchQueueRelease(__v) (dispatch_release(__v));
17
+ #else
18
+ // -fobjc-arc
19
+ #define FMDBAutorelease(__v)
20
+ #define FMDBReturnAutoreleased(__v) (__v)
21
+
22
+ #define FMDBRetain(__v)
23
+ #define FMDBReturnRetained(__v) (__v)
24
+
25
+ #define FMDBRelease(__v)
26
+
27
+ #if TARGET_OS_IPHONE
28
+ // Compiling for iOS
29
+ #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000
30
+ // iOS 6.0 or later
31
+ #define FMDBDispatchQueueRelease(__v)
32
+ #else
33
+ // iOS 5.X or earlier
34
+ #define FMDBDispatchQueueRelease(__v) (dispatch_release(__v));
35
+ #endif
36
+ #else
37
+ // Compiling for Mac OS X
38
+ #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
39
+ // Mac OS X 10.8 or later
40
+ #define FMDBDispatchQueueRelease(__v)
41
+ #else
42
+ // Mac OS X 10.7 or earlier
43
+ #define FMDBDispatchQueueRelease(__v) (dispatch_release(__v));
44
+ #endif
45
+ #endif
46
+ #endif
47
+
48
+
49
+ @interface FMDatabase : NSObject {
50
+
51
+ sqlite3* _db;
52
+ NSString* _databasePath;
53
+ BOOL _logsErrors;
54
+ BOOL _crashOnErrors;
55
+ BOOL _traceExecution;
56
+ BOOL _checkedOut;
57
+ BOOL _shouldCacheStatements;
58
+ BOOL _isExecutingStatement;
59
+ BOOL _inTransaction;
60
+ int _busyRetryTimeout;
61
+
62
+ NSMutableDictionary *_cachedStatements;
63
+ NSMutableSet *_openResultSets;
64
+ NSMutableSet *_openFunctions;
65
+
66
+ }
67
+
68
+
69
+ @property (atomic, assign) BOOL traceExecution;
70
+ @property (atomic, assign) BOOL checkedOut;
71
+ @property (atomic, assign) int busyRetryTimeout;
72
+ @property (atomic, assign) BOOL crashOnErrors;
73
+ @property (atomic, assign) BOOL logsErrors;
74
+ @property (atomic, retain) NSMutableDictionary *cachedStatements;
75
+
76
+
77
+ + (id)databaseWithPath:(NSString*)inPath;
78
+ - (id)initWithPath:(NSString*)inPath;
79
+
80
+ - (BOOL)open;
81
+ #if SQLITE_VERSION_NUMBER >= 3005000
82
+ - (BOOL)openWithFlags:(int)flags;
83
+ #endif
84
+ - (BOOL)close;
85
+ - (BOOL)goodConnection;
86
+ - (void)clearCachedStatements;
87
+ - (void)closeOpenResultSets;
88
+ - (BOOL)hasOpenResultSets;
89
+
90
+ // encryption methods. You need to have purchased the sqlite encryption extensions for these to work.
91
+ - (BOOL)setKey:(NSString*)key;
92
+ - (BOOL)rekey:(NSString*)key;
93
+
94
+ - (NSString *)databasePath;
95
+
96
+ - (NSString*)lastErrorMessage;
97
+
98
+ - (int)lastErrorCode;
99
+ - (BOOL)hadError;
100
+ - (NSError*)lastError;
101
+
102
+ - (sqlite_int64)lastInsertRowId;
103
+
104
+ - (sqlite3*)sqliteHandle;
105
+
106
+ - (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ...;
107
+ - (BOOL)executeUpdate:(NSString*)sql, ...;
108
+ - (BOOL)executeUpdateWithFormat:(NSString *)format, ...;
109
+ - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments;
110
+ - (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments;
111
+
112
+ - (FMResultSet *)executeQuery:(NSString*)sql, ...;
113
+ - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...;
114
+ - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments;
115
+ - (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments;
116
+
117
+ - (BOOL)rollback;
118
+ - (BOOL)commit;
119
+ - (BOOL)beginTransaction;
120
+ - (BOOL)beginDeferredTransaction;
121
+ - (BOOL)inTransaction;
122
+ - (BOOL)shouldCacheStatements;
123
+ - (void)setShouldCacheStatements:(BOOL)value;
124
+
125
+ #if SQLITE_VERSION_NUMBER >= 3007000
126
+ - (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr;
127
+ - (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr;
128
+ - (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr;
129
+ - (NSError*)inSavePoint:(void (^)(BOOL *rollback))block;
130
+ #endif
131
+
132
+ + (BOOL)isSQLiteThreadSafe;
133
+ + (NSString*)sqliteLibVersion;
134
+
135
+ - (int)changes;
136
+
137
+ - (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(sqlite3_context *context, int argc, sqlite3_value **argv))block;
138
+
139
+ @end
140
+
141
+ @interface FMStatement : NSObject {
142
+ sqlite3_stmt *_statement;
143
+ NSString *_query;
144
+ long _useCount;
145
+ }
146
+
147
+ @property (atomic, assign) long useCount;
148
+ @property (atomic, retain) NSString *query;
149
+ @property (atomic, assign) sqlite3_stmt *statement;
150
+
151
+ - (void)close;
152
+ - (void)reset;
153
+
154
+ @end
155
+
@@ -0,0 +1,1162 @@
1
+ #import "FMDatabase.h"
2
+ #import "unistd.h"
3
+ #import <objc/runtime.h>
4
+
5
+ @interface FMDatabase ()
6
+
7
+ - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args;
8
+ - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args;
9
+ @end
10
+
11
+ @implementation FMDatabase
12
+ @synthesize cachedStatements=_cachedStatements;
13
+ @synthesize logsErrors=_logsErrors;
14
+ @synthesize crashOnErrors=_crashOnErrors;
15
+ @synthesize busyRetryTimeout=_busyRetryTimeout;
16
+ @synthesize checkedOut=_checkedOut;
17
+ @synthesize traceExecution=_traceExecution;
18
+
19
+ + (id)databaseWithPath:(NSString*)aPath {
20
+ return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]);
21
+ }
22
+
23
+ + (NSString*)sqliteLibVersion {
24
+ return [NSString stringWithFormat:@"%s", sqlite3_libversion()];
25
+ }
26
+
27
+ + (BOOL)isSQLiteThreadSafe {
28
+ // make sure to read the sqlite headers on this guy!
29
+ return sqlite3_threadsafe() != 0;
30
+ }
31
+
32
+ - (id)initWithPath:(NSString*)aPath {
33
+
34
+ assert(sqlite3_threadsafe()); // whoa there big boy- gotta make sure sqlite it happy with what we're going to do.
35
+
36
+ self = [super init];
37
+
38
+ if (self) {
39
+ _databasePath = [aPath copy];
40
+ _openResultSets = [[NSMutableSet alloc] init];
41
+ _db = 0x00;
42
+ _logsErrors = 0x00;
43
+ _crashOnErrors = 0x00;
44
+ _busyRetryTimeout = 0x00;
45
+ }
46
+
47
+ return self;
48
+ }
49
+
50
+ - (void)finalize {
51
+ [self close];
52
+ [super finalize];
53
+ }
54
+
55
+ - (void)dealloc {
56
+ [self close];
57
+ FMDBRelease(_openResultSets);
58
+ FMDBRelease(_cachedStatements);
59
+ FMDBRelease(_databasePath);
60
+ FMDBRelease(_openFunctions);
61
+
62
+ #if ! __has_feature(objc_arc)
63
+ [super dealloc];
64
+ #endif
65
+ }
66
+
67
+ - (NSString *)databasePath {
68
+ return _databasePath;
69
+ }
70
+
71
+ - (sqlite3*)sqliteHandle {
72
+ return _db;
73
+ }
74
+
75
+ - (const char*)sqlitePath {
76
+
77
+ if (!_databasePath) {
78
+ return ":memory:";
79
+ }
80
+
81
+ if ([_databasePath length] == 0) {
82
+ return ""; // this creates a temporary database (it's an sqlite thing).
83
+ }
84
+
85
+ return [_databasePath fileSystemRepresentation];
86
+
87
+ }
88
+
89
+ - (BOOL)open {
90
+ if (_db) {
91
+ return YES;
92
+ }
93
+
94
+ int err = sqlite3_open([self sqlitePath], &_db );
95
+ if(err != SQLITE_OK) {
96
+ NSLog(@"error opening!: %d", err);
97
+ return NO;
98
+ }
99
+
100
+ return YES;
101
+ }
102
+
103
+ #if SQLITE_VERSION_NUMBER >= 3005000
104
+ - (BOOL)openWithFlags:(int)flags {
105
+ int err = sqlite3_open_v2([self sqlitePath], &_db, flags, NULL /* Name of VFS module to use */);
106
+ if(err != SQLITE_OK) {
107
+ NSLog(@"error opening!: %d", err);
108
+ return NO;
109
+ }
110
+ return YES;
111
+ }
112
+ #endif
113
+
114
+
115
+ - (BOOL)close {
116
+
117
+ [self clearCachedStatements];
118
+ [self closeOpenResultSets];
119
+
120
+ if (!_db) {
121
+ return YES;
122
+ }
123
+
124
+ int rc;
125
+ BOOL retry;
126
+ int numberOfRetries = 0;
127
+ BOOL triedFinalizingOpenStatements = NO;
128
+
129
+ do {
130
+ retry = NO;
131
+ rc = sqlite3_close(_db);
132
+
133
+ if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
134
+
135
+ retry = YES;
136
+ usleep(20);
137
+
138
+ if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) {
139
+ NSLog(@"%s:%d", __FUNCTION__, __LINE__);
140
+ NSLog(@"Database busy, unable to close");
141
+ return NO;
142
+ }
143
+
144
+ if (!triedFinalizingOpenStatements) {
145
+ triedFinalizingOpenStatements = YES;
146
+ sqlite3_stmt *pStmt;
147
+ while ((pStmt = sqlite3_next_stmt(_db, 0x00)) !=0) {
148
+ NSLog(@"Closing leaked statement");
149
+ sqlite3_finalize(pStmt);
150
+ }
151
+ }
152
+ }
153
+ else if (SQLITE_OK != rc) {
154
+ NSLog(@"error closing!: %d", rc);
155
+ }
156
+ }
157
+ while (retry);
158
+
159
+ _db = nil;
160
+ return YES;
161
+ }
162
+
163
+ - (void)clearCachedStatements {
164
+
165
+ for (FMStatement *cachedStmt in [_cachedStatements objectEnumerator]) {
166
+ [cachedStmt close];
167
+ }
168
+
169
+ [_cachedStatements removeAllObjects];
170
+ }
171
+
172
+ - (BOOL)hasOpenResultSets {
173
+ return [_openResultSets count] > 0;
174
+ }
175
+
176
+ - (void)closeOpenResultSets {
177
+
178
+ //Copy the set so we don't get mutation errors
179
+ NSMutableSet *openSetCopy = FMDBReturnAutoreleased([_openResultSets copy]);
180
+ for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) {
181
+ FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue];
182
+
183
+ [rs setParentDB:nil];
184
+ [rs close];
185
+
186
+ [_openResultSets removeObject:rsInWrappedInATastyValueMeal];
187
+ }
188
+ }
189
+
190
+ - (void)resultSetDidClose:(FMResultSet *)resultSet {
191
+ NSValue *setValue = [NSValue valueWithNonretainedObject:resultSet];
192
+
193
+ [_openResultSets removeObject:setValue];
194
+ }
195
+
196
+ - (FMStatement*)cachedStatementForQuery:(NSString*)query {
197
+ return [_cachedStatements objectForKey:query];
198
+ }
199
+
200
+ - (void)setCachedStatement:(FMStatement*)statement forQuery:(NSString*)query {
201
+
202
+ query = [query copy]; // in case we got handed in a mutable string...
203
+
204
+ [statement setQuery:query];
205
+
206
+ [_cachedStatements setObject:statement forKey:query];
207
+
208
+ FMDBRelease(query);
209
+ }
210
+
211
+
212
+ - (BOOL)rekey:(NSString*)key {
213
+ #ifdef SQLITE_HAS_CODEC
214
+ if (!key) {
215
+ return NO;
216
+ }
217
+
218
+ int rc = sqlite3_rekey(_db, [key UTF8String], (int)strlen([key UTF8String]));
219
+
220
+ if (rc != SQLITE_OK) {
221
+ NSLog(@"error on rekey: %d", rc);
222
+ NSLog(@"%@", [self lastErrorMessage]);
223
+ }
224
+
225
+ return (rc == SQLITE_OK);
226
+ #else
227
+ return NO;
228
+ #endif
229
+ }
230
+
231
+ - (BOOL)setKey:(NSString*)key {
232
+ #ifdef SQLITE_HAS_CODEC
233
+ if (!key) {
234
+ return NO;
235
+ }
236
+
237
+ int rc = sqlite3_key(_db, [key UTF8String], (int)strlen([key UTF8String]));
238
+
239
+ return (rc == SQLITE_OK);
240
+ #else
241
+ return NO;
242
+ #endif
243
+ }
244
+
245
+ - (BOOL)goodConnection {
246
+
247
+ if (!_db) {
248
+ return NO;
249
+ }
250
+
251
+ FMResultSet *rs = [self executeQuery:@"select name from sqlite_master where type='table'"];
252
+
253
+ if (rs) {
254
+ [rs close];
255
+ return YES;
256
+ }
257
+
258
+ return NO;
259
+ }
260
+
261
+ - (void)warnInUse {
262
+ NSLog(@"The FMDatabase %@ is currently in use.", self);
263
+
264
+ #ifndef NS_BLOCK_ASSERTIONS
265
+ if (_crashOnErrors) {
266
+ NSAssert1(false, @"The FMDatabase %@ is currently in use.", self);
267
+ abort();
268
+ }
269
+ #endif
270
+ }
271
+
272
+ - (BOOL)databaseExists {
273
+
274
+ if (!_db) {
275
+
276
+ NSLog(@"The FMDatabase %@ is not open.", self);
277
+
278
+ #ifndef NS_BLOCK_ASSERTIONS
279
+ if (_crashOnErrors) {
280
+ NSAssert1(false, @"The FMDatabase %@ is not open.", self);
281
+ abort();
282
+ }
283
+ #endif
284
+
285
+ return NO;
286
+ }
287
+
288
+ return YES;
289
+ }
290
+
291
+ - (NSString*)lastErrorMessage {
292
+ return [NSString stringWithUTF8String:sqlite3_errmsg(_db)];
293
+ }
294
+
295
+ - (BOOL)hadError {
296
+ int lastErrCode = [self lastErrorCode];
297
+
298
+ return (lastErrCode > SQLITE_OK && lastErrCode < SQLITE_ROW);
299
+ }
300
+
301
+ - (int)lastErrorCode {
302
+ return sqlite3_errcode(_db);
303
+ }
304
+
305
+
306
+ - (NSError*)errorWithMessage:(NSString*)message {
307
+ NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey];
308
+
309
+ return [NSError errorWithDomain:@"FMDatabase" code:sqlite3_errcode(_db) userInfo:errorMessage];
310
+ }
311
+
312
+ - (NSError*)lastError {
313
+ return [self errorWithMessage:[self lastErrorMessage]];
314
+ }
315
+
316
+ - (sqlite_int64)lastInsertRowId {
317
+
318
+ if (_isExecutingStatement) {
319
+ [self warnInUse];
320
+ return NO;
321
+ }
322
+
323
+ _isExecutingStatement = YES;
324
+
325
+ sqlite_int64 ret = sqlite3_last_insert_rowid(_db);
326
+
327
+ _isExecutingStatement = NO;
328
+
329
+ return ret;
330
+ }
331
+
332
+ - (int)changes {
333
+ if (_isExecutingStatement) {
334
+ [self warnInUse];
335
+ return 0;
336
+ }
337
+
338
+ _isExecutingStatement = YES;
339
+
340
+ int ret = sqlite3_changes(_db);
341
+
342
+ _isExecutingStatement = NO;
343
+
344
+ return ret;
345
+ }
346
+
347
+ - (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt {
348
+
349
+ if ((!obj) || ((NSNull *)obj == [NSNull null])) {
350
+ sqlite3_bind_null(pStmt, idx);
351
+ }
352
+
353
+ // FIXME - someday check the return codes on these binds.
354
+ else if ([obj isKindOfClass:[NSData class]]) {
355
+ const void *bytes = [obj bytes];
356
+ if (!bytes) {
357
+ // it's an empty NSData object, aka [NSData data].
358
+ // Don't pass a NULL pointer, or sqlite will bind a SQL null instead of a blob.
359
+ bytes = "";
360
+ }
361
+ sqlite3_bind_blob(pStmt, idx, bytes, (int)[obj length], SQLITE_STATIC);
362
+ }
363
+ else if ([obj isKindOfClass:[NSDate class]]) {
364
+ sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]);
365
+ }
366
+ else if ([obj isKindOfClass:[NSNumber class]]) {
367
+
368
+ if (strcmp([obj objCType], @encode(BOOL)) == 0) {
369
+ sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0));
370
+ }
371
+ else if (strcmp([obj objCType], @encode(int)) == 0) {
372
+ sqlite3_bind_int64(pStmt, idx, [obj longValue]);
373
+ }
374
+ else if (strcmp([obj objCType], @encode(long)) == 0) {
375
+ sqlite3_bind_int64(pStmt, idx, [obj longValue]);
376
+ }
377
+ else if (strcmp([obj objCType], @encode(long long)) == 0) {
378
+ sqlite3_bind_int64(pStmt, idx, [obj longLongValue]);
379
+ }
380
+ else if (strcmp([obj objCType], @encode(unsigned long long)) == 0) {
381
+ sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedLongLongValue]);
382
+ }
383
+ else if (strcmp([obj objCType], @encode(float)) == 0) {
384
+ sqlite3_bind_double(pStmt, idx, [obj floatValue]);
385
+ }
386
+ else if (strcmp([obj objCType], @encode(double)) == 0) {
387
+ sqlite3_bind_double(pStmt, idx, [obj doubleValue]);
388
+ }
389
+ else {
390
+ sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
391
+ }
392
+ }
393
+ else {
394
+ sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
395
+ }
396
+ }
397
+
398
+ - (void)extractSQL:(NSString *)sql argumentsList:(va_list)args intoString:(NSMutableString *)cleanedSQL arguments:(NSMutableArray *)arguments {
399
+
400
+ NSUInteger length = [sql length];
401
+ unichar last = '\0';
402
+ for (NSUInteger i = 0; i < length; ++i) {
403
+ id arg = nil;
404
+ unichar current = [sql characterAtIndex:i];
405
+ unichar add = current;
406
+ if (last == '%') {
407
+ switch (current) {
408
+ case '@':
409
+ arg = va_arg(args, id);
410
+ break;
411
+ case 'c':
412
+ // warning: second argument to 'va_arg' is of promotable type 'char'; this va_arg has undefined behavior because arguments will be promoted to 'int'
413
+ arg = [NSString stringWithFormat:@"%c", va_arg(args, int)];
414
+ break;
415
+ case 's':
416
+ arg = [NSString stringWithUTF8String:va_arg(args, char*)];
417
+ break;
418
+ case 'd':
419
+ case 'D':
420
+ case 'i':
421
+ arg = [NSNumber numberWithInt:va_arg(args, int)];
422
+ break;
423
+ case 'u':
424
+ case 'U':
425
+ arg = [NSNumber numberWithUnsignedInt:va_arg(args, unsigned int)];
426
+ break;
427
+ case 'h':
428
+ i++;
429
+ if (i < length && [sql characterAtIndex:i] == 'i') {
430
+ // warning: second argument to 'va_arg' is of promotable type 'short'; this va_arg has undefined behavior because arguments will be promoted to 'int'
431
+ arg = [NSNumber numberWithShort:(short)(va_arg(args, int))];
432
+ }
433
+ else if (i < length && [sql characterAtIndex:i] == 'u') {
434
+ // warning: second argument to 'va_arg' is of promotable type 'unsigned short'; this va_arg has undefined behavior because arguments will be promoted to 'int'
435
+ arg = [NSNumber numberWithUnsignedShort:(unsigned short)(va_arg(args, uint))];
436
+ }
437
+ else {
438
+ i--;
439
+ }
440
+ break;
441
+ case 'q':
442
+ i++;
443
+ if (i < length && [sql characterAtIndex:i] == 'i') {
444
+ arg = [NSNumber numberWithLongLong:va_arg(args, long long)];
445
+ }
446
+ else if (i < length && [sql characterAtIndex:i] == 'u') {
447
+ arg = [NSNumber numberWithUnsignedLongLong:va_arg(args, unsigned long long)];
448
+ }
449
+ else {
450
+ i--;
451
+ }
452
+ break;
453
+ case 'f':
454
+ arg = [NSNumber numberWithDouble:va_arg(args, double)];
455
+ break;
456
+ case 'g':
457
+ // warning: second argument to 'va_arg' is of promotable type 'float'; this va_arg has undefined behavior because arguments will be promoted to 'double'
458
+ arg = [NSNumber numberWithFloat:(float)(va_arg(args, double))];
459
+ break;
460
+ case 'l':
461
+ i++;
462
+ if (i < length) {
463
+ unichar next = [sql characterAtIndex:i];
464
+ if (next == 'l') {
465
+ i++;
466
+ if (i < length && [sql characterAtIndex:i] == 'd') {
467
+ //%lld
468
+ arg = [NSNumber numberWithLongLong:va_arg(args, long long)];
469
+ }
470
+ else if (i < length && [sql characterAtIndex:i] == 'u') {
471
+ //%llu
472
+ arg = [NSNumber numberWithUnsignedLongLong:va_arg(args, unsigned long long)];
473
+ }
474
+ else {
475
+ i--;
476
+ }
477
+ }
478
+ else if (next == 'd') {
479
+ //%ld
480
+ arg = [NSNumber numberWithLong:va_arg(args, long)];
481
+ }
482
+ else if (next == 'u') {
483
+ //%lu
484
+ arg = [NSNumber numberWithUnsignedLong:va_arg(args, unsigned long)];
485
+ }
486
+ else {
487
+ i--;
488
+ }
489
+ }
490
+ else {
491
+ i--;
492
+ }
493
+ break;
494
+ default:
495
+ // something else that we can't interpret. just pass it on through like normal
496
+ break;
497
+ }
498
+ }
499
+ else if (current == '%') {
500
+ // percent sign; skip this character
501
+ add = '\0';
502
+ }
503
+
504
+ if (arg != nil) {
505
+ [cleanedSQL appendString:@"?"];
506
+ [arguments addObject:arg];
507
+ }
508
+ else if (add != '\0') {
509
+ [cleanedSQL appendFormat:@"%C", add];
510
+ }
511
+ last = current;
512
+ }
513
+ }
514
+
515
+ - (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments {
516
+ return [self executeQuery:sql withArgumentsInArray:nil orDictionary:arguments orVAList:nil];
517
+ }
518
+
519
+ - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
520
+
521
+ if (![self databaseExists]) {
522
+ return 0x00;
523
+ }
524
+
525
+ if (_isExecutingStatement) {
526
+ [self warnInUse];
527
+ return 0x00;
528
+ }
529
+
530
+ _isExecutingStatement = YES;
531
+
532
+ int rc = 0x00;
533
+ sqlite3_stmt *pStmt = 0x00;
534
+ FMStatement *statement = 0x00;
535
+ FMResultSet *rs = 0x00;
536
+
537
+ if (_traceExecution && sql) {
538
+ NSLog(@"%@ executeQuery: %@", self, sql);
539
+ }
540
+
541
+ if (_shouldCacheStatements) {
542
+ statement = [self cachedStatementForQuery:sql];
543
+ pStmt = statement ? [statement statement] : 0x00;
544
+ [statement reset];
545
+ }
546
+
547
+ int numberOfRetries = 0;
548
+ BOOL retry = NO;
549
+
550
+ if (!pStmt) {
551
+ do {
552
+ retry = NO;
553
+ rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
554
+
555
+ if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
556
+ retry = YES;
557
+ usleep(20);
558
+
559
+ if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) {
560
+ NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
561
+ NSLog(@"Database busy");
562
+ sqlite3_finalize(pStmt);
563
+ _isExecutingStatement = NO;
564
+ return nil;
565
+ }
566
+ }
567
+ else if (SQLITE_OK != rc) {
568
+
569
+ if (_logsErrors) {
570
+ NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
571
+ NSLog(@"DB Query: %@", sql);
572
+ NSLog(@"DB Path: %@", _databasePath);
573
+ #ifndef NS_BLOCK_ASSERTIONS
574
+ if (_crashOnErrors) {
575
+ abort();
576
+ NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
577
+ }
578
+ #endif
579
+ }
580
+
581
+ sqlite3_finalize(pStmt);
582
+ _isExecutingStatement = NO;
583
+ return nil;
584
+ }
585
+ }
586
+ while (retry);
587
+ }
588
+
589
+ id obj;
590
+ int idx = 0;
591
+ int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!)
592
+
593
+ // If dictionaryArgs is passed in, that means we are using sqlite's named parameter support
594
+ if (dictionaryArgs) {
595
+
596
+ for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
597
+
598
+ // Prefix the key with a colon.
599
+ NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
600
+
601
+ // Get the index for the parameter name.
602
+ int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
603
+
604
+ FMDBRelease(parameterName);
605
+
606
+ if (namedIdx > 0) {
607
+ // Standard binding from here.
608
+ [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
609
+ // increment the binding count, so our check below works out
610
+ idx++;
611
+ }
612
+ else {
613
+ NSLog(@"Could not find index for %@", dictionaryKey);
614
+ }
615
+ }
616
+ }
617
+ else {
618
+
619
+ while (idx < queryCount) {
620
+
621
+ if (arrayArgs) {
622
+ obj = [arrayArgs objectAtIndex:(NSUInteger)idx];
623
+ }
624
+ else {
625
+ obj = va_arg(args, id);
626
+ }
627
+
628
+ if (_traceExecution) {
629
+ NSLog(@"obj: %@", obj);
630
+ }
631
+
632
+ idx++;
633
+
634
+ [self bindObject:obj toColumn:idx inStatement:pStmt];
635
+ }
636
+ }
637
+
638
+ if (idx != queryCount) {
639
+ NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)");
640
+ sqlite3_finalize(pStmt);
641
+ _isExecutingStatement = NO;
642
+ return nil;
643
+ }
644
+
645
+ FMDBRetain(statement); // to balance the release below
646
+
647
+ if (!statement) {
648
+ statement = [[FMStatement alloc] init];
649
+ [statement setStatement:pStmt];
650
+
651
+ if (_shouldCacheStatements) {
652
+ [self setCachedStatement:statement forQuery:sql];
653
+ }
654
+ }
655
+
656
+ // the statement gets closed in rs's dealloc or [rs close];
657
+ rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self];
658
+ [rs setQuery:sql];
659
+
660
+ NSValue *openResultSet = [NSValue valueWithNonretainedObject:rs];
661
+ [_openResultSets addObject:openResultSet];
662
+
663
+ [statement setUseCount:[statement useCount] + 1];
664
+
665
+ FMDBRelease(statement);
666
+
667
+ _isExecutingStatement = NO;
668
+
669
+ return rs;
670
+ }
671
+
672
+ - (FMResultSet *)executeQuery:(NSString*)sql, ... {
673
+ va_list args;
674
+ va_start(args, sql);
675
+
676
+ id result = [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args];
677
+
678
+ va_end(args);
679
+ return result;
680
+ }
681
+
682
+ - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... {
683
+ va_list args;
684
+ va_start(args, format);
685
+
686
+ NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]];
687
+ NSMutableArray *arguments = [NSMutableArray array];
688
+ [self extractSQL:format argumentsList:args intoString:sql arguments:arguments];
689
+
690
+ va_end(args);
691
+
692
+ return [self executeQuery:sql withArgumentsInArray:arguments];
693
+ }
694
+
695
+ - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments {
696
+ return [self executeQuery:sql withArgumentsInArray:arguments orDictionary:nil orVAList:nil];
697
+ }
698
+
699
+ - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {
700
+
701
+ if (![self databaseExists]) {
702
+ return NO;
703
+ }
704
+
705
+ if (_isExecutingStatement) {
706
+ [self warnInUse];
707
+ return NO;
708
+ }
709
+
710
+ _isExecutingStatement = YES;
711
+
712
+ int rc = 0x00;
713
+ sqlite3_stmt *pStmt = 0x00;
714
+ FMStatement *cachedStmt = 0x00;
715
+
716
+ if (_traceExecution && sql) {
717
+ NSLog(@"%@ executeUpdate: %@", self, sql);
718
+ }
719
+
720
+ if (_shouldCacheStatements) {
721
+ cachedStmt = [self cachedStatementForQuery:sql];
722
+ pStmt = cachedStmt ? [cachedStmt statement] : 0x00;
723
+ [cachedStmt reset];
724
+ }
725
+
726
+ int numberOfRetries = 0;
727
+ BOOL retry = NO;
728
+
729
+ if (!pStmt) {
730
+
731
+ do {
732
+ retry = NO;
733
+ rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
734
+ if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
735
+ retry = YES;
736
+ usleep(20);
737
+
738
+ if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) {
739
+ NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
740
+ NSLog(@"Database busy");
741
+ sqlite3_finalize(pStmt);
742
+ _isExecutingStatement = NO;
743
+ return NO;
744
+ }
745
+ }
746
+ else if (SQLITE_OK != rc) {
747
+
748
+ if (_logsErrors) {
749
+ NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
750
+ NSLog(@"DB Query: %@", sql);
751
+ NSLog(@"DB Path: %@", _databasePath);
752
+ #ifndef NS_BLOCK_ASSERTIONS
753
+ if (_crashOnErrors) {
754
+ abort();
755
+ NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
756
+ }
757
+ #endif
758
+ }
759
+
760
+ sqlite3_finalize(pStmt);
761
+
762
+ if (outErr) {
763
+ *outErr = [self errorWithMessage:[NSString stringWithUTF8String:sqlite3_errmsg(_db)]];
764
+ }
765
+
766
+ _isExecutingStatement = NO;
767
+ return NO;
768
+ }
769
+ }
770
+ while (retry);
771
+ }
772
+
773
+ id obj;
774
+ int idx = 0;
775
+ int queryCount = sqlite3_bind_parameter_count(pStmt);
776
+
777
+ // If dictionaryArgs is passed in, that means we are using sqlite's named parameter support
778
+ if (dictionaryArgs) {
779
+
780
+ for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
781
+
782
+ // Prefix the key with a colon.
783
+ NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
784
+
785
+ // Get the index for the parameter name.
786
+ int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
787
+
788
+ FMDBRelease(parameterName);
789
+
790
+ if (namedIdx > 0) {
791
+ // Standard binding from here.
792
+ [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
793
+
794
+ // increment the binding count, so our check below works out
795
+ idx++;
796
+ }
797
+ else {
798
+ NSLog(@"Could not find index for %@", dictionaryKey);
799
+ }
800
+ }
801
+ }
802
+ else {
803
+
804
+ while (idx < queryCount) {
805
+
806
+ if (arrayArgs) {
807
+ obj = [arrayArgs objectAtIndex:(NSUInteger)idx];
808
+ }
809
+ else {
810
+ obj = va_arg(args, id);
811
+ }
812
+
813
+ if (_traceExecution) {
814
+ NSLog(@"obj: %@", obj);
815
+ }
816
+
817
+ idx++;
818
+
819
+ [self bindObject:obj toColumn:idx inStatement:pStmt];
820
+ }
821
+ }
822
+
823
+
824
+ if (idx != queryCount) {
825
+ NSLog(@"Error: the bind count (%d) is not correct for the # of variables in the query (%d) (%@) (executeUpdate)", idx, queryCount, sql);
826
+ sqlite3_finalize(pStmt);
827
+ _isExecutingStatement = NO;
828
+ return NO;
829
+ }
830
+
831
+ /* Call sqlite3_step() to run the virtual machine. Since the SQL being
832
+ ** executed is not a SELECT statement, we assume no data will be returned.
833
+ */
834
+ numberOfRetries = 0;
835
+
836
+ do {
837
+ rc = sqlite3_step(pStmt);
838
+ retry = NO;
839
+
840
+ if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
841
+ // this will happen if the db is locked, like if we are doing an update or insert.
842
+ // in that case, retry the step... and maybe wait just 10 milliseconds.
843
+ retry = YES;
844
+ if (SQLITE_LOCKED == rc) {
845
+ rc = sqlite3_reset(pStmt);
846
+ if (rc != SQLITE_LOCKED) {
847
+ NSLog(@"Unexpected result from sqlite3_reset (%d) eu", rc);
848
+ }
849
+ }
850
+ usleep(20);
851
+
852
+ if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) {
853
+ NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
854
+ NSLog(@"Database busy");
855
+ retry = NO;
856
+ }
857
+ }
858
+ else if (SQLITE_DONE == rc) {
859
+ // all is well, let's return.
860
+ }
861
+ else if (SQLITE_ERROR == rc) {
862
+ NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db));
863
+ NSLog(@"DB Query: %@", sql);
864
+ }
865
+ else if (SQLITE_MISUSE == rc) {
866
+ // uh oh.
867
+ NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db));
868
+ NSLog(@"DB Query: %@", sql);
869
+ }
870
+ else {
871
+ // wtf?
872
+ NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db));
873
+ NSLog(@"DB Query: %@", sql);
874
+ }
875
+
876
+ } while (retry);
877
+
878
+ if (rc == SQLITE_ROW) {
879
+ NSAssert1(NO, @"A executeUpdate is being called with a query string '%@'", sql);
880
+ }
881
+
882
+ if (_shouldCacheStatements && !cachedStmt) {
883
+ cachedStmt = [[FMStatement alloc] init];
884
+
885
+ [cachedStmt setStatement:pStmt];
886
+
887
+ [self setCachedStatement:cachedStmt forQuery:sql];
888
+
889
+ FMDBRelease(cachedStmt);
890
+ }
891
+
892
+ int closeErrorCode;
893
+
894
+ if (cachedStmt) {
895
+ [cachedStmt setUseCount:[cachedStmt useCount] + 1];
896
+ closeErrorCode = sqlite3_reset(pStmt);
897
+ }
898
+ else {
899
+ /* Finalize the virtual machine. This releases all memory and other
900
+ ** resources allocated by the sqlite3_prepare() call above.
901
+ */
902
+ closeErrorCode = sqlite3_finalize(pStmt);
903
+ }
904
+
905
+ if (closeErrorCode != SQLITE_OK) {
906
+ NSLog(@"Unknown error finalizing or resetting statement (%d: %s)", closeErrorCode, sqlite3_errmsg(_db));
907
+ NSLog(@"DB Query: %@", sql);
908
+ }
909
+
910
+ _isExecutingStatement = NO;
911
+ return (rc == SQLITE_DONE || rc == SQLITE_OK);
912
+ }
913
+
914
+
915
+ - (BOOL)executeUpdate:(NSString*)sql, ... {
916
+ va_list args;
917
+ va_start(args, sql);
918
+
919
+ BOOL result = [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:nil orVAList:args];
920
+
921
+ va_end(args);
922
+ return result;
923
+ }
924
+
925
+ - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments {
926
+ return [self executeUpdate:sql error:nil withArgumentsInArray:arguments orDictionary:nil orVAList:nil];
927
+ }
928
+
929
+ - (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments {
930
+ return [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:arguments orVAList:nil];
931
+ }
932
+
933
+ - (BOOL)executeUpdateWithFormat:(NSString*)format, ... {
934
+ va_list args;
935
+ va_start(args, format);
936
+
937
+ NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]];
938
+ NSMutableArray *arguments = [NSMutableArray array];
939
+
940
+ [self extractSQL:format argumentsList:args intoString:sql arguments:arguments];
941
+
942
+ va_end(args);
943
+
944
+ return [self executeUpdate:sql withArgumentsInArray:arguments];
945
+ }
946
+
947
+ - (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ... {
948
+ va_list args;
949
+ va_start(args, outErr);
950
+
951
+ BOOL result = [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:args];
952
+
953
+ va_end(args);
954
+ return result;
955
+ }
956
+
957
+ - (BOOL)rollback {
958
+ BOOL b = [self executeUpdate:@"rollback transaction"];
959
+
960
+ if (b) {
961
+ _inTransaction = NO;
962
+ }
963
+
964
+ return b;
965
+ }
966
+
967
+ - (BOOL)commit {
968
+ BOOL b = [self executeUpdate:@"commit transaction"];
969
+
970
+ if (b) {
971
+ _inTransaction = NO;
972
+ }
973
+
974
+ return b;
975
+ }
976
+
977
+ - (BOOL)beginDeferredTransaction {
978
+
979
+ BOOL b = [self executeUpdate:@"begin deferred transaction"];
980
+ if (b) {
981
+ _inTransaction = YES;
982
+ }
983
+
984
+ return b;
985
+ }
986
+
987
+ - (BOOL)beginTransaction {
988
+
989
+ BOOL b = [self executeUpdate:@"begin exclusive transaction"];
990
+ if (b) {
991
+ _inTransaction = YES;
992
+ }
993
+
994
+ return b;
995
+ }
996
+
997
+ - (BOOL)inTransaction {
998
+ return _inTransaction;
999
+ }
1000
+
1001
+ #if SQLITE_VERSION_NUMBER >= 3007000
1002
+
1003
+ - (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr {
1004
+
1005
+ // FIXME: make sure the savepoint name doesn't have a ' in it.
1006
+
1007
+ NSParameterAssert(name);
1008
+
1009
+ if (![self executeUpdate:[NSString stringWithFormat:@"savepoint '%@';", name]]) {
1010
+
1011
+ if (outErr) {
1012
+ *outErr = [self lastError];
1013
+ }
1014
+
1015
+ return NO;
1016
+ }
1017
+
1018
+ return YES;
1019
+ }
1020
+
1021
+ - (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr {
1022
+
1023
+ NSParameterAssert(name);
1024
+
1025
+ BOOL worked = [self executeUpdate:[NSString stringWithFormat:@"release savepoint '%@';", name]];
1026
+
1027
+ if (!worked && outErr) {
1028
+ *outErr = [self lastError];
1029
+ }
1030
+
1031
+ return worked;
1032
+ }
1033
+
1034
+ - (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr {
1035
+
1036
+ NSParameterAssert(name);
1037
+
1038
+ BOOL worked = [self executeUpdate:[NSString stringWithFormat:@"rollback transaction to savepoint '%@';", name]];
1039
+
1040
+ if (!worked && *outErr) {
1041
+ *outErr = [self lastError];
1042
+ }
1043
+
1044
+ return worked;
1045
+ }
1046
+
1047
+ - (NSError*)inSavePoint:(void (^)(BOOL *rollback))block {
1048
+ static unsigned long savePointIdx = 0;
1049
+
1050
+ NSString *name = [NSString stringWithFormat:@"dbSavePoint%ld", savePointIdx++];
1051
+
1052
+ BOOL shouldRollback = NO;
1053
+
1054
+ NSError *err = 0x00;
1055
+
1056
+ if (![self startSavePointWithName:name error:&err]) {
1057
+ return err;
1058
+ }
1059
+
1060
+ block(&shouldRollback);
1061
+
1062
+ if (shouldRollback) {
1063
+ [self rollbackToSavePointWithName:name error:&err];
1064
+ }
1065
+ else {
1066
+ [self releaseSavePointWithName:name error:&err];
1067
+ }
1068
+
1069
+ return err;
1070
+ }
1071
+
1072
+ #endif
1073
+
1074
+
1075
+ - (BOOL)shouldCacheStatements {
1076
+ return _shouldCacheStatements;
1077
+ }
1078
+
1079
+ - (void)setShouldCacheStatements:(BOOL)value {
1080
+
1081
+ _shouldCacheStatements = value;
1082
+
1083
+ if (_shouldCacheStatements && !_cachedStatements) {
1084
+ [self setCachedStatements:[NSMutableDictionary dictionary]];
1085
+ }
1086
+
1087
+ if (!_shouldCacheStatements) {
1088
+ [self setCachedStatements:nil];
1089
+ }
1090
+ }
1091
+
1092
+ void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv);
1093
+ void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv) {
1094
+ #if ! __has_feature(objc_arc)
1095
+ void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (id)sqlite3_user_data(context);
1096
+ #else
1097
+ void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (__bridge id)sqlite3_user_data(context);
1098
+ #endif
1099
+ block(context, argc, argv);
1100
+ }
1101
+
1102
+
1103
+ - (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(sqlite3_context *context, int argc, sqlite3_value **argv))block {
1104
+
1105
+ if (!_openFunctions) {
1106
+ _openFunctions = [NSMutableSet new];
1107
+ }
1108
+
1109
+ id b = FMDBReturnAutoreleased([block copy]);
1110
+
1111
+ [_openFunctions addObject:b];
1112
+
1113
+ /* I tried adding custom functions to release the block when the connection is destroyed- but they seemed to never be called, so we use _openFunctions to store the values instead. */
1114
+ #if ! __has_feature(objc_arc)
1115
+ sqlite3_create_function([self sqliteHandle], [name UTF8String], count, SQLITE_UTF8, (void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00);
1116
+ #else
1117
+ sqlite3_create_function([self sqliteHandle], [name UTF8String], count, SQLITE_UTF8, (__bridge void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00);
1118
+ #endif
1119
+ }
1120
+
1121
+ @end
1122
+
1123
+
1124
+
1125
+ @implementation FMStatement
1126
+ @synthesize statement=_statement;
1127
+ @synthesize query=_query;
1128
+ @synthesize useCount=_useCount;
1129
+
1130
+ - (void)finalize {
1131
+ [self close];
1132
+ [super finalize];
1133
+ }
1134
+
1135
+ - (void)dealloc {
1136
+ [self close];
1137
+ FMDBRelease(_query);
1138
+ #if ! __has_feature(objc_arc)
1139
+ [super dealloc];
1140
+ #endif
1141
+ }
1142
+
1143
+ - (void)close {
1144
+ if (_statement) {
1145
+ sqlite3_finalize(_statement);
1146
+ _statement = 0x00;
1147
+ }
1148
+ }
1149
+
1150
+ - (void)reset {
1151
+ if (_statement) {
1152
+ sqlite3_reset(_statement);
1153
+ }
1154
+ }
1155
+
1156
+ - (NSString*)description {
1157
+ return [NSString stringWithFormat:@"%@ %ld hit(s) for query %@", [super description], _useCount, _query];
1158
+ }
1159
+
1160
+
1161
+ @end
1162
+