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