]> git.saurik.com Git - apple/security.git/blob - Analytics/SQLite/SFSQLiteStatement.m
Security-58286.1.32.tar.gz
[apple/security.git] / Analytics / SQLite / SFSQLiteStatement.m
1 /*
2 * Copyright (c) 2017 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #import "SFSQLite.h"
25 #import "SFSQLiteStatement.h"
26 #import "SFObjCType.h"
27
28 @interface SFSQLiteStatement ()
29 @property (nonatomic, strong) NSMutableArray *temporaryBoundObjects;
30 @end
31 @implementation SFSQLiteStatement
32
33 @synthesize SQLite = _SQLite;
34 @synthesize SQL = _SQL;
35 @synthesize handle = _handle;
36 @synthesize reset = _reset;
37 @synthesize temporaryBoundObjects = _temporaryBoundObjects;
38
39 - (id)initWithSQLite:(SFSQLite *)SQLite SQL:(NSString *)SQL handle:(sqlite3_stmt *)handle {
40 if ((self = [super init])) {
41 _SQLite = SQLite;
42 _SQL = SQL;
43 _handle = handle;
44 _reset = YES;
45 }
46 return self;
47 }
48
49 - (void)finalizeStatement {
50 SFSQLite *strongSQLite = _SQLite;
51
52 if (!_reset) {
53 [strongSQLite raise: @"Statement not reset after last use: \"%@\"", _SQL];
54 }
55 if (sqlite3_finalize(_handle)) {
56 [strongSQLite raise:@"Error finalizing prepared statement: \"%@\"", _SQL];
57 }
58 }
59
60 - (void)resetAfterStepError
61 {
62 if (!_reset) {
63 (void)sqlite3_reset(_handle); // we expect this to return an error
64 (void)sqlite3_clear_bindings(_handle);
65 [_temporaryBoundObjects removeAllObjects];
66 _reset = YES;
67 }
68 }
69
70 - (BOOL)step {
71 if (_reset) {
72 _reset = NO;
73 }
74
75 int rc = sqlite3_step(_handle);
76 if ((rc & 0x00FF) == SQLITE_ROW) {
77 return YES;
78 } else if ((rc & 0x00FF) == SQLITE_DONE) {
79 return NO;
80 } else {
81 [self resetAfterStepError];
82 [_SQLite raise:@"Failed to step (%d): \"%@\"", rc, _SQL];
83 return NO;
84 }
85 }
86
87 - (void)reset {
88 SFSQLite *strongSQLite = _SQLite;
89
90 if (!_reset) {
91 if (sqlite3_reset(_handle)) {
92 [strongSQLite raise:@"Error resetting prepared statement: \"%@\"", _SQL];
93 }
94
95 if (sqlite3_clear_bindings(_handle)) {
96 [strongSQLite raise:@"Error clearing prepared statement bindings: \"%@\"", _SQL];
97 }
98 [_temporaryBoundObjects removeAllObjects];
99 _reset = YES;
100 }
101 }
102
103 - (void)bindInt:(SInt32)value atIndex:(NSUInteger)index {
104 NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL);
105
106 if (sqlite3_bind_int(_handle, (int)index+1, value)) {
107 [_SQLite raise:@"Error binding int at %ld: \"%@\"", (unsigned long)index, _SQL];
108 }
109 }
110
111 - (void)bindInt64:(SInt64)value atIndex:(NSUInteger)index {
112 NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL);
113
114 if (sqlite3_bind_int64(_handle, (int)index+1, value)) {
115 [_SQLite raise:@"Error binding int64 at %ld: \"%@\"", (unsigned long)index, _SQL];
116 }
117 }
118
119 - (void)bindDouble:(double)value atIndex:(NSUInteger)index {
120 NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL);
121
122 if (sqlite3_bind_double(_handle, (int)index+1, value)) {
123 [_SQLite raise:@"Error binding double at %ld: \"%@\"", (unsigned long)index, _SQL];
124 }
125 }
126
127 - (void)bindBlob:(NSData *)value atIndex:(NSUInteger)index {
128 NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL);
129
130 if (value) {
131 NS_VALID_UNTIL_END_OF_SCOPE NSData *arcSafeValue = value;
132 if (sqlite3_bind_blob(_handle, (int)index+1, [arcSafeValue bytes], (int)[arcSafeValue length], NULL)) {
133 [_SQLite raise:@"Error binding blob at %ld: \"%@\"", (unsigned long)index, _SQL];
134 }
135 } else {
136 [self bindNullAtIndex:index];
137 }
138 }
139
140 - (void)bindText:(NSString *)value atIndex:(NSUInteger)index {
141 NSAssert(_reset, @"Statement is not reset: \"%@\"", _SQL);
142
143 if (value) {
144 NS_VALID_UNTIL_END_OF_SCOPE NSString *arcSafeValue = value;
145 if (sqlite3_bind_text(_handle, (int)index+1, [arcSafeValue UTF8String], -1, NULL)) {
146 [_SQLite raise:@"Error binding text at %ld: \"%@\"", (unsigned long)index, _SQL];
147 }
148 } else {
149 [self bindNullAtIndex:index];
150 }
151 }
152
153 - (void)bindNullAtIndex:(NSUInteger)index {
154 int rc = sqlite3_bind_null(_handle, (int)index+1);
155 if ((rc & 0x00FF) != SQLITE_OK) {
156 [_SQLite raise:@"sqlite3_bind_null error"];
157 }
158 }
159
160 - (id)retainedTemporaryBoundObject:(id)object
161 {
162 if (!_temporaryBoundObjects) {
163 _temporaryBoundObjects = [NSMutableArray new];
164 }
165 [_temporaryBoundObjects addObject:object];
166 return object;
167 }
168
169 - (void)bindValue:(id)value atIndex:(NSUInteger)index {
170 if ([value isKindOfClass:[NSNumber class]]) {
171 SFObjCType *type = [SFObjCType typeForValue:value];
172 if (type.isIntegerNumber) {
173 if (type.size <= 4) {
174 [self bindInt:[value intValue] atIndex:index];
175 } else {
176 [self bindInt64:[value longLongValue] atIndex:index];
177 }
178 } else {
179 NSAssert(type.isFloatingPointNumber, @"Expected number type to be either integer or floating point");
180 if (type.code == SFObjCTypeFloat) {
181 [self bindInt:[value intValue] atIndex:index];
182 } else {
183 NSAssert(type.code == SFObjCTypeDouble, @"Unexpected floating point number type: %@", type);
184 [self bindInt64:[value longLongValue] atIndex:index];
185 }
186 }
187 } else if ([value isKindOfClass:[NSData class]]) {
188 [self bindBlob:value atIndex:index];
189 } else if ([value isKindOfClass:[NSUUID class]]) {
190 uuid_t uuid;
191 [(NSUUID *)value getUUIDBytes:uuid];
192 [self bindBlob:[self retainedTemporaryBoundObject:[NSData dataWithBytes:uuid length:sizeof(uuid_t)]] atIndex:index];
193 } else if ([value isKindOfClass:[NSString class]]) {
194 [self bindText:value atIndex:index];
195 } else if ([value isKindOfClass:[NSNull class]]) {
196 [self bindNullAtIndex:index];
197 } else if ([value isKindOfClass:[NSDate class]]) {
198 [self bindDouble:[(NSDate *)value timeIntervalSinceReferenceDate] atIndex:index];
199 } else if ([value isKindOfClass:[NSError class]]) {
200 [self bindBlob:[self retainedTemporaryBoundObject:[NSKeyedArchiver archivedDataWithRootObject:value]] atIndex:index];
201 } else if ([value isKindOfClass:[NSURL class]]) {
202 [self bindText:[self retainedTemporaryBoundObject:[value absoluteString]] atIndex:index];
203 } else {
204 [NSException raise:NSInvalidArgumentException format:@"Can't bind object of type %@", [value class]];
205 }
206 }
207
208 - (void)bindValues:(NSArray *)values {
209 for (NSUInteger i = 0; i < values.count; i++) {
210 [self bindValue:values[i] atIndex:i];
211 }
212 }
213
214 - (NSUInteger)columnCount {
215 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
216
217 return sqlite3_column_count(_handle);
218 }
219
220 - (int)columnTypeAtIndex:(NSUInteger)index {
221 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
222
223 return sqlite3_column_type(_handle, (int)index);
224 }
225
226 - (NSString *)columnNameAtIndex:(NSUInteger)index {
227 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
228
229 return @(sqlite3_column_name(_handle, (int)index));
230 }
231
232 - (SInt32)intAtIndex:(NSUInteger)index {
233 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
234
235 return sqlite3_column_int(_handle, (int)index);
236 }
237
238 - (SInt64)int64AtIndex:(NSUInteger)index {
239 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
240
241 return sqlite3_column_int64(_handle, (int)index);
242 }
243
244 - (double)doubleAtIndex:(NSUInteger)index {
245 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
246
247 return sqlite3_column_double(_handle, (int)index);
248 }
249
250 - (NSData *)blobAtIndex:(NSUInteger)index {
251 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
252
253 const void *bytes = sqlite3_column_blob(_handle, (int)index);
254 if (bytes) {
255 int length = sqlite3_column_bytes(_handle, (int)index);
256 return [NSData dataWithBytes:bytes length:length];
257 } else {
258 return nil;
259 }
260 }
261
262 - (NSString *)textAtIndex:(NSUInteger)index {
263 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
264
265 const char *text = (const char *)sqlite3_column_text(_handle, (int)index);
266 if (text) {
267 return @(text);
268 } else {
269 return nil;
270 }
271 }
272
273 - (id)objectAtIndex:(NSUInteger)index {
274 int type = [self columnTypeAtIndex:index];
275 switch (type) {
276 case SQLITE_INTEGER:
277 return @([self int64AtIndex:index]);
278
279 case SQLITE_FLOAT:
280 return @([self doubleAtIndex:index]);
281
282 case SQLITE_TEXT:
283 return [self textAtIndex:index];
284
285 case SQLITE_BLOB:
286 return [self blobAtIndex:index];
287
288 case SQLITE_NULL:
289 return nil;
290
291 default:
292 [NSException raise:NSGenericException format:@"Unexpected column type: %d", type];
293 return nil;
294 }
295 }
296
297 - (NSArray *)allObjects {
298 NSUInteger columnCount = [self columnCount];
299 NSMutableArray *objects = [NSMutableArray arrayWithCapacity:columnCount];
300 for (NSUInteger i = 0; i < columnCount; i++) {
301 objects[i] = [self objectAtIndex:i] ?: [NSNull null];
302 }
303 return objects;
304 }
305
306 - (NSDictionary *)allObjectsByColumnName {
307 NSUInteger columnCount = [self columnCount];
308 NSMutableDictionary *objectsByColumnName = [NSMutableDictionary dictionaryWithCapacity:columnCount];
309 for (NSUInteger i = 0; i < columnCount; i++) {
310 NSString *columnName = [self columnNameAtIndex:i];
311 id object = [self objectAtIndex:i];
312 if (object) {
313 objectsByColumnName[columnName] = object;
314 }
315 }
316 return objectsByColumnName;
317 }
318
319 @end