2  * Copyright (c) 2017 Apple Inc. All Rights Reserved.
 
   4  * @APPLE_LICENSE_HEADER_START@
 
   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
 
  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.
 
  21  * @APPLE_LICENSE_HEADER_END@
 
  25 #import "SFSQLiteStatement.h"
 
  26 #import "SFObjCType.h"
 
  29 @interface SFSQLiteStatement ()
 
  30 @property (nonatomic, strong) NSMutableArray *temporaryBoundObjects;
 
  32 @implementation SFSQLiteStatement
 
  34 @synthesize SQLite = _SQLite;
 
  35 @synthesize SQL = _SQL;
 
  36 @synthesize handle = _handle;
 
  37 @synthesize reset = _reset;
 
  38 @synthesize temporaryBoundObjects = _temporaryBoundObjects;
 
  40 - (id)initWithSQLite:(SFSQLite *)SQLite SQL:(NSString *)SQL handle:(sqlite3_stmt *)handle {
 
  41     if ((self = [super init])) {
 
  50 - (void)finalizeStatement {
 
  52         secerror("sfsqlite: Statement not reset after last use: \"%@\"", _SQL);
 
  55     if (sqlite3_finalize(_handle)) {
 
  56         secerror("sfsqlite: Error finalizing prepared statement: \"%@\"", _SQL);
 
  61 - (void)resetAfterStepError
 
  64         (void)sqlite3_reset(_handle); // we expect this to return an error
 
  65         (void)sqlite3_clear_bindings(_handle);
 
  66         [_temporaryBoundObjects removeAllObjects];
 
  76     int rc = sqlite3_step(_handle);
 
  77     if ((rc & 0x00FF) == SQLITE_ROW) {
 
  79     } else if ((rc & 0x00FF) == SQLITE_DONE) {
 
  82         [self resetAfterStepError];
 
  83         secerror("sfsqlite: Failed to step (%d): \"%@\"", rc, _SQL);
 
  90         if (sqlite3_reset(_handle)) {
 
  91             secerror("sfsqlite: Error resetting prepared statement: \"%@\"", _SQL);
 
  95         if (sqlite3_clear_bindings(_handle)) {
 
  96             secerror("sfsqlite: Error clearing prepared statement bindings: \"%@\"", _SQL);
 
  99         [_temporaryBoundObjects removeAllObjects];
 
 104 - (void)bindInt:(SInt32)value atIndex:(NSUInteger)index {
 
 106         secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
 
 110     if (sqlite3_bind_int(_handle, (int)index+1, value)) {
 
 111         secerror("sfsqlite: Error binding int at %ld: \"%@\"", (unsigned long)index, _SQL);
 
 116 - (void)bindInt64:(SInt64)value atIndex:(NSUInteger)index {
 
 118         secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
 
 122     if (sqlite3_bind_int64(_handle, (int)index+1, value)) {
 
 123         secerror("sfsqlite: Error binding int64 at %ld: \"%@\"", (unsigned long)index, _SQL);
 
 128 - (void)bindDouble:(double)value atIndex:(NSUInteger)index {
 
 130         secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
 
 134     if (sqlite3_bind_double(_handle, (int)index+1, value)) {
 
 135         secerror("sfsqlite: Error binding double at %ld: \"%@\"", (unsigned long)index, _SQL);
 
 140 - (void)bindBlob:(NSData *)value atIndex:(NSUInteger)index {
 
 142         secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
 
 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)) {
 
 149             secerror("sfsqlite: Error binding blob at %ld: \"%@\"", (unsigned long)index, _SQL);
 
 153         [self bindNullAtIndex:index];
 
 157 - (void)bindText:(NSString *)value atIndex:(NSUInteger)index {
 
 159         secerror("sfsqlite: Statement is not reset: \"%@\"", _SQL);
 
 164         NS_VALID_UNTIL_END_OF_SCOPE NSString *arcSafeValue = value;
 
 165         if (sqlite3_bind_text(_handle, (int)index+1, [arcSafeValue UTF8String], -1, NULL)) {
 
 166             secerror("sfsqlite: Error binding text at %ld: \"%@\"", (unsigned long)index, _SQL);
 
 170         [self bindNullAtIndex:index];
 
 174 - (void)bindNullAtIndex:(NSUInteger)index {
 
 175     int rc = sqlite3_bind_null(_handle, (int)index+1);
 
 176     if ((rc & 0x00FF) != SQLITE_OK) {
 
 177         secerror("sfsqlite: sqlite3_bind_null error");
 
 182 - (id)retainedTemporaryBoundObject:(id)object
 
 184     if (!_temporaryBoundObjects) {
 
 185         _temporaryBoundObjects = [NSMutableArray new];
 
 187     [_temporaryBoundObjects addObject:object];
 
 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];
 
 198                 [self bindInt64:[value longLongValue] atIndex:index];
 
 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];
 
 205                 NSAssert(type.code == SFObjCTypeDouble, @"Unexpected floating point number type: %@", type);
 
 206                 [self bindInt64:[value longLongValue] atIndex:index];
 
 209     } else if ([value isKindOfClass:[NSData class]]) {
 
 210         [self bindBlob:value atIndex:index];
 
 211     } else if ([value isKindOfClass:[NSUUID class]]) {
 
 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];
 
 226         secerror("sfsqlite: Can't bind object of type %@", [value class]);
 
 231 - (void)bindValues:(NSArray *)values {
 
 232     for (NSUInteger i = 0; i < values.count; i++) {
 
 233         [self bindValue:values[i] atIndex:i];
 
 237 - (NSUInteger)columnCount {
 
 238     NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
 
 240     return sqlite3_column_count(_handle);
 
 243 - (int)columnTypeAtIndex:(NSUInteger)index {
 
 244     NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
 
 246     return sqlite3_column_type(_handle, (int)index);
 
 249 - (NSString *)columnNameAtIndex:(NSUInteger)index {
 
 250     NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
 
 252     return @(sqlite3_column_name(_handle, (int)index));
 
 255 - (SInt32)intAtIndex:(NSUInteger)index {
 
 256     NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
 
 258     return sqlite3_column_int(_handle, (int)index);
 
 261 - (SInt64)int64AtIndex:(NSUInteger)index {
 
 262     NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
 
 264     return sqlite3_column_int64(_handle, (int)index);
 
 267 - (double)doubleAtIndex:(NSUInteger)index {
 
 268     NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
 
 270     return sqlite3_column_double(_handle, (int)index);
 
 273 - (NSData *)blobAtIndex:(NSUInteger)index {
 
 274     NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
 
 276     const void *bytes = sqlite3_column_blob(_handle, (int)index);
 
 278         int length = sqlite3_column_bytes(_handle, (int)index);
 
 279         return [NSData dataWithBytes:bytes length:length];
 
 285 - (NSString *)textAtIndex:(NSUInteger)index {
 
 286     NSAssert(!_reset, @"Statement is reset: \"%@\"", _SQL);
 
 288     const char *text = (const char *)sqlite3_column_text(_handle, (int)index);
 
 296 - (id)objectAtIndex:(NSUInteger)index {
 
 297     int type = [self columnTypeAtIndex:index];
 
 300             return @([self int64AtIndex:index]);
 
 303             return @([self doubleAtIndex:index]);
 
 306             return [self textAtIndex:index];
 
 309             return [self blobAtIndex:index];
 
 315             secerror("sfsqlite: Unexpected column type: %d", type);
 
 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];
 
 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];
 
 336             objectsByColumnName[columnName] = object;
 
 339     return objectsByColumnName;