]> git.saurik.com Git - apple/security.git/blob - Analytics/SQLite/SFSQLiteStatement.m
Security-58286.51.6.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 #if __OBJC2__
25
26 #import <Foundation/NSKeyedArchiver_Private.h>
27 #import "SFSQLite.h"
28 #import "SFSQLiteStatement.h"
29 #import "SFObjCType.h"
30 #import "debugging.h"
31
32 @interface SFSQLiteStatement ()
33 @property (nonatomic, strong) NSMutableArray *temporaryBoundObjects;
34 @end
35 @implementation SFSQLiteStatement
36
37 @synthesize SQLite = _SQLite;
38 @synthesize SQL = _SQL;
39 @synthesize handle = _handle;
40 @synthesize reset = _reset;
41 @synthesize temporaryBoundObjects = _temporaryBoundObjects;
42
43 - (id)initWithSQLite:(SFSQLite *)SQLite SQL:(NSString *)SQL handle:(sqlite3_stmt *)handle {
44 if ((self = [super init])) {
45 _SQLite = SQLite;
46 _SQL = SQL;
47 _handle = handle;
48 _reset = YES;
49 }
50 return self;
51 }
52
53 - (void)finalizeStatement {
54 if (!_reset) {
55 secerror("sfsqlite: Statement not reset after last use: \"%@\"", _SQL);
56 return;
57 }
58 if (sqlite3_finalize(_handle)) {
59 secerror("sfsqlite: Error finalizing prepared statement: \"%@\"", _SQL);
60 return;
61 }
62 }
63
64 - (void)resetAfterStepError
65 {
66 if (!_reset) {
67 (void)sqlite3_reset(_handle); // we expect this to return an error
68 (void)sqlite3_clear_bindings(_handle);
69 [_temporaryBoundObjects removeAllObjects];
70 _reset = YES;
71 }
72 }
73
74 - (BOOL)step {
75 if (_reset) {
76 _reset = NO;
77 }
78
79 int rc = sqlite3_step(_handle);
80 if ((rc & 0x00FF) == SQLITE_ROW) {
81 return YES;
82 } else if ((rc & 0x00FF) == SQLITE_DONE) {
83 return NO;
84 } else {
85 [self resetAfterStepError];
86 secerror("sfsqlite: Failed to step (%d): \"%@\"", rc, _SQL);
87 return NO;
88 }
89 }
90
91 - (void)reset {
92 if (!_reset) {
93 if (sqlite3_reset(_handle)) {
94 secerror("sfsqlite: Error resetting prepared statement: \"%@\"", _SQL);
95 return;
96 }
97
98 if (sqlite3_clear_bindings(_handle)) {
99 secerror("sfsqlite: Error clearing prepared statement bindings: \"%@\"", _SQL);
100 return;
101 }
102 [_temporaryBoundObjects removeAllObjects];
103 _reset = YES;
104 }
105 }
106
107 - (void)bindInt:(SInt32)value atIndex:(NSUInteger)index {
108 if (!_reset) {
109 secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
110 return;
111 }
112
113 if (sqlite3_bind_int(_handle, (int)index+1, value)) {
114 secerror("sfsqlite: Error binding int at %ld: \"%@\"", (unsigned long)index, _SQL);
115 return;
116 }
117 }
118
119 - (void)bindInt64:(SInt64)value atIndex:(NSUInteger)index {
120 if (!_reset) {
121 secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
122 return;
123 }
124
125 if (sqlite3_bind_int64(_handle, (int)index+1, value)) {
126 secerror("sfsqlite: Error binding int64 at %ld: \"%@\"", (unsigned long)index, _SQL);
127 return;
128 }
129 }
130
131 - (void)bindDouble:(double)value atIndex:(NSUInteger)index {
132 if (!_reset) {
133 secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
134 return;
135 }
136
137 if (sqlite3_bind_double(_handle, (int)index+1, value)) {
138 secerror("sfsqlite: Error binding double at %ld: \"%@\"", (unsigned long)index, _SQL);
139 return;
140 }
141 }
142
143 - (void)bindBlob:(NSData *)value atIndex:(NSUInteger)index {
144 if (!_reset) {
145 secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
146 return;
147 }
148
149 if (value) {
150 NS_VALID_UNTIL_END_OF_SCOPE NSData *arcSafeValue = value;
151 if (sqlite3_bind_blob(_handle, (int)index+1, [arcSafeValue bytes], (int)[arcSafeValue length], NULL)) {
152 secerror("sfsqlite: Error binding blob at %ld: \"%@\"", (unsigned long)index, _SQL);
153 return;
154 }
155 } else {
156 [self bindNullAtIndex:index];
157 }
158 }
159
160 - (void)bindText:(NSString *)value atIndex:(NSUInteger)index {
161 if (!_reset) {
162 secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
163 return;
164 }
165
166 if (value) {
167 NS_VALID_UNTIL_END_OF_SCOPE NSString *arcSafeValue = value;
168 if (sqlite3_bind_text(_handle, (int)index+1, [arcSafeValue UTF8String], -1, NULL)) {
169 secerror("sfsqlite: Error binding text at %ld: \"%@\"", (unsigned long)index, _SQL);
170 return;
171 }
172 } else {
173 [self bindNullAtIndex:index];
174 }
175 }
176
177 - (void)bindNullAtIndex:(NSUInteger)index {
178 int rc = sqlite3_bind_null(_handle, (int)index+1);
179 if ((rc & 0x00FF) != SQLITE_OK) {
180 secerror("sfsqlite: sqlite3_bind_null error");
181 return;
182 }
183 }
184
185 - (id)retainedTemporaryBoundObject:(id)object
186 {
187 if (!_temporaryBoundObjects) {
188 _temporaryBoundObjects = [NSMutableArray new];
189 }
190 [_temporaryBoundObjects addObject:object];
191 return object;
192 }
193
194 - (void)bindValue:(id)value atIndex:(NSUInteger)index {
195 if ([value isKindOfClass:[NSNumber class]]) {
196 SFObjCType *type = [SFObjCType typeForValue:value];
197 if (type.isIntegerNumber) {
198 if (type.size <= 4) {
199 [self bindInt:[value intValue] atIndex:index];
200 } else {
201 [self bindInt64:[value longLongValue] atIndex:index];
202 }
203 } else {
204 NSAssert(type.isFloatingPointNumber, @"Expected number type to be either integer or floating point");
205 NSAssert(type.code == SFObjCTypeDouble || type.code == SFObjCTypeFloat, @"Unexpected floating point number type: %@", type);
206 [self bindDouble:[value doubleValue] atIndex:index];
207 }
208 } else if ([value isKindOfClass:[NSData class]]) {
209 [self bindBlob:value atIndex:index];
210 } else if ([value isKindOfClass:[NSUUID class]]) {
211 uuid_t uuid;
212 [(NSUUID *)value getUUIDBytes:uuid];
213 [self bindBlob:[self retainedTemporaryBoundObject:[NSData dataWithBytes:uuid length:sizeof(uuid_t)]] atIndex:index];
214 } else if ([value isKindOfClass:[NSString class]]) {
215 [self bindText:value atIndex:index];
216 } else if ([value isKindOfClass:[NSNull class]]) {
217 [self bindNullAtIndex:index];
218 } else if ([value isKindOfClass:[NSDate class]]) {
219 [self bindDouble:[(NSDate *)value timeIntervalSinceReferenceDate] atIndex:index];
220 } else if ([value isKindOfClass:[NSError class]]) {
221 [self bindBlob:[self retainedTemporaryBoundObject:[NSKeyedArchiver archivedDataWithRootObject:value requiringSecureCoding:YES error:nil]] atIndex:index];
222 } else if ([value isKindOfClass:[NSURL class]]) {
223 [self bindText:[self retainedTemporaryBoundObject:[value absoluteString]] atIndex:index];
224 } else {
225 secerror("sfsqlite: Can't bind object of type %@", [value class]);
226 return;
227 }
228 }
229
230 - (void)bindValues:(NSArray *)values {
231 for (NSUInteger i = 0; i < values.count; i++) {
232 [self bindValue:values[i] atIndex:i];
233 }
234 }
235
236 - (NSUInteger)columnCount {
237 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
238
239 return sqlite3_column_count(_handle);
240 }
241
242 - (int)columnTypeAtIndex:(NSUInteger)index {
243 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
244
245 return sqlite3_column_type(_handle, (int)index);
246 }
247
248 - (NSString *)columnNameAtIndex:(NSUInteger)index {
249 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
250
251 return @(sqlite3_column_name(_handle, (int)index));
252 }
253
254 - (SInt32)intAtIndex:(NSUInteger)index {
255 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
256
257 return sqlite3_column_int(_handle, (int)index);
258 }
259
260 - (SInt64)int64AtIndex:(NSUInteger)index {
261 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
262
263 return sqlite3_column_int64(_handle, (int)index);
264 }
265
266 - (double)doubleAtIndex:(NSUInteger)index {
267 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
268
269 return sqlite3_column_double(_handle, (int)index);
270 }
271
272 - (NSData *)blobAtIndex:(NSUInteger)index {
273 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
274
275 const void *bytes = sqlite3_column_blob(_handle, (int)index);
276 if (bytes) {
277 int length = sqlite3_column_bytes(_handle, (int)index);
278 return [NSData dataWithBytes:bytes length:length];
279 } else {
280 return nil;
281 }
282 }
283
284 - (NSString *)textAtIndex:(NSUInteger)index {
285 NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
286
287 const char *text = (const char *)sqlite3_column_text(_handle, (int)index);
288 if (text) {
289 return @(text);
290 } else {
291 return nil;
292 }
293 }
294
295 - (id)objectAtIndex:(NSUInteger)index {
296 int type = [self columnTypeAtIndex:index];
297 switch (type) {
298 case SQLITE_INTEGER:
299 return @([self int64AtIndex:index]);
300
301 case SQLITE_FLOAT:
302 return @([self doubleAtIndex:index]);
303
304 case SQLITE_TEXT:
305 return [self textAtIndex:index];
306
307 case SQLITE_BLOB:
308 return [self blobAtIndex:index];
309
310 case SQLITE_NULL:
311 return nil;
312
313 default:
314 secerror("sfsqlite: Unexpected column type: %d", type);
315 return nil;
316 }
317 }
318
319 - (NSArray *)allObjects {
320 NSUInteger columnCount = [self columnCount];
321 NSMutableArray *objects = [NSMutableArray arrayWithCapacity:columnCount];
322 for (NSUInteger i = 0; i < columnCount; i++) {
323 objects[i] = [self objectAtIndex:i] ?: [NSNull null];
324 }
325 return objects;
326 }
327
328 - (NSDictionary *)allObjectsByColumnName {
329 NSUInteger columnCount = [self columnCount];
330 NSMutableDictionary *objectsByColumnName = [NSMutableDictionary dictionaryWithCapacity:columnCount];
331 for (NSUInteger i = 0; i < columnCount; i++) {
332 NSString *columnName = [self columnNameAtIndex:i];
333 id object = [self objectAtIndex:i];
334 if (object) {
335 objectsByColumnName[columnName] = object;
336 }
337 }
338 return objectsByColumnName;
339 }
340
341 @end
342
343 #endif