diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c224b21 --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +Mars + +Copyright (C) 2013 Matt Ronge +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the Mars project nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/Mars/Mars.xcodeproj/project.pbxproj b/Mars/Mars.xcodeproj/project.pbxproj index 1e7eb36..35b3098 100644 --- a/Mars/Mars.xcodeproj/project.pbxproj +++ b/Mars/Mars.xcodeproj/project.pbxproj @@ -7,12 +7,20 @@ objects = { /* Begin PBXBuildFile section */ + 15256F7E1C29CB9400E56634 /* MDatabase.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F898883116DAF60100B5ED12 /* MDatabase.h */; }; + 15256F7F1C29CB9400E56634 /* MQuery.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F898884016DAF6E200B5ED12 /* MQuery.h */; }; + 15256F801C29CB9400E56634 /* MConnection.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F898883316DAF60200B5ED12 /* MConnection.h */; }; + 15256F811C29CB9400E56634 /* Mars.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F898880E16DAF59400B5ED12 /* Mars.h */; }; + 15256F821C29CB9400E56634 /* MSelectQuery.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F898884316DAFA5300B5ED12 /* MSelectQuery.h */; }; + 15256F831C29CB9400E56634 /* MInsertQuery.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F898885316DB156400B5ED12 /* MInsertQuery.h */; }; + 15256F841C29CB9400E56634 /* MUpdateQuery.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F8DE4AB216DB17AA00F5F426 /* MUpdateQuery.h */; }; + 15256F851C29CB9400E56634 /* MDeleteQuery.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F8DE4AB516DB1AB700F5F426 /* MDeleteQuery.h */; }; + 15256F861C29CB9400E56634 /* CTLogger.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F898883D16DAF67400B5ED12 /* CTLogger.h */; }; + 15256F881C29CB9400E56634 /* MDatabase+AsyncAdditions.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C07ADE8ABA30C0CC9A8B33D7 /* MDatabase+AsyncAdditions.h */; }; C07AD390CA27F1FB3BF7C675 /* MDatabase+AsyncAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = C07ADE8ABA30C0CC9A8B33D7 /* MDatabase+AsyncAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; C07ADA022259BBC98EBE16D5 /* MDatabase+AsyncAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = C07ADB33DDA93701C9CD6E57 /* MDatabase+AsyncAdditions.m */; }; F898880A16DAF59400B5ED12 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F898880916DAF59400B5ED12 /* Foundation.framework */; }; F898880F16DAF59400B5ED12 /* Mars.h in Headers */ = {isa = PBXBuildFile; fileRef = F898880E16DAF59400B5ED12 /* Mars.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F898881916DAF59500B5ED12 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F898881816DAF59500B5ED12 /* SenTestingKit.framework */; }; - F898881B16DAF59500B5ED12 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F898881A16DAF59500B5ED12 /* UIKit.framework */; }; F898881C16DAF59500B5ED12 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F898880916DAF59400B5ED12 /* Foundation.framework */; }; F898881F16DAF59500B5ED12 /* libMars.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F898880616DAF59400B5ED12 /* libMars.a */; }; F898882516DAF59500B5ED12 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F898882316DAF59500B5ED12 /* InfoPlist.strings */; }; @@ -24,8 +32,6 @@ F898884516DAFA5300B5ED12 /* MSelectQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = F898884416DAFA5300B5ED12 /* MSelectQuery.m */; }; F898884916DAFF7F00B5ED12 /* NSDictionary+Mars.m in Sources */ = {isa = PBXBuildFile; fileRef = F898884816DAFF7F00B5ED12 /* NSDictionary+Mars.m */; }; F898884F16DB0F1200B5ED12 /* QueryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F898884E16DB0F1200B5ED12 /* QueryTests.m */; }; - F898885116DB11AB00B5ED12 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F898885016DB11AA00B5ED12 /* libsqlite3.dylib */; }; - F898885216DB11B900B5ED12 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F898885016DB11AA00B5ED12 /* libsqlite3.dylib */; }; F898885516DB156500B5ED12 /* MInsertQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = F898885416DB156400B5ED12 /* MInsertQuery.m */; }; F8DE4AB416DB17AA00F5F426 /* MUpdateQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DE4AB316DB17AA00F5F426 /* MUpdateQuery.m */; }; F8DE4AB716DB1AB700F5F426 /* MDeleteQuery.m in Sources */ = {isa = PBXBuildFile; fileRef = F8DE4AB616DB1AB700F5F426 /* MDeleteQuery.m */; }; @@ -49,6 +55,28 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 15256F7D1C29CB5A00E56634 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = ../../include/Mars; + dstSubfolderSpec = 16; + files = ( + 15256F7E1C29CB9400E56634 /* MDatabase.h in CopyFiles */, + 15256F7F1C29CB9400E56634 /* MQuery.h in CopyFiles */, + 15256F801C29CB9400E56634 /* MConnection.h in CopyFiles */, + 15256F811C29CB9400E56634 /* Mars.h in CopyFiles */, + 15256F821C29CB9400E56634 /* MSelectQuery.h in CopyFiles */, + 15256F831C29CB9400E56634 /* MInsertQuery.h in CopyFiles */, + 15256F841C29CB9400E56634 /* MUpdateQuery.h in CopyFiles */, + 15256F851C29CB9400E56634 /* MDeleteQuery.h in CopyFiles */, + 15256F861C29CB9400E56634 /* CTLogger.h in CopyFiles */, + 15256F881C29CB9400E56634 /* MDatabase+AsyncAdditions.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ C07ADB33DDA93701C9CD6E57 /* MDatabase+AsyncAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MDatabase+AsyncAdditions.m"; sourceTree = ""; }; C07ADDE8399B0B50FAE585D1 /* MDatabase+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MDatabase+Private.h"; sourceTree = ""; }; @@ -57,9 +85,7 @@ F898880916DAF59400B5ED12 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; F898880D16DAF59400B5ED12 /* Mars-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Mars-Prefix.pch"; sourceTree = ""; }; F898880E16DAF59400B5ED12 /* Mars.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Mars.h; sourceTree = ""; }; - F898881716DAF59500B5ED12 /* MarsTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MarsTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; - F898881816DAF59500B5ED12 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; - F898881A16DAF59500B5ED12 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + F898881716DAF59500B5ED12 /* MarsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MarsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F898882216DAF59500B5ED12 /* MarsTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MarsTests-Info.plist"; sourceTree = ""; }; F898882416DAF59500B5ED12 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; F898882616DAF59500B5ED12 /* MarsTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MarsTests.h; sourceTree = ""; }; @@ -79,7 +105,6 @@ F898884C16DB045000B5ED12 /* MQuery+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MQuery+Private.h"; sourceTree = ""; }; F898884D16DB0F1200B5ED12 /* QueryTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QueryTests.h; sourceTree = ""; }; F898884E16DB0F1200B5ED12 /* QueryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QueryTests.m; sourceTree = ""; }; - F898885016DB11AA00B5ED12 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; F898885316DB156400B5ED12 /* MInsertQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MInsertQuery.h; sourceTree = ""; }; F898885416DB156400B5ED12 /* MInsertQuery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MInsertQuery.m; sourceTree = ""; }; F8DE4AB216DB17AA00F5F426 /* MUpdateQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MUpdateQuery.h; sourceTree = ""; }; @@ -93,7 +118,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F898885116DB11AB00B5ED12 /* libsqlite3.dylib in Frameworks */, F898880A16DAF59400B5ED12 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -102,9 +126,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F898885216DB11B900B5ED12 /* libsqlite3.dylib in Frameworks */, - F898881916DAF59500B5ED12 /* SenTestingKit.framework in Frameworks */, - F898881B16DAF59500B5ED12 /* UIKit.framework in Frameworks */, F898881C16DAF59500B5ED12 /* Foundation.framework in Frameworks */, F898881F16DAF59500B5ED12 /* libMars.a in Frameworks */, ); @@ -116,7 +137,6 @@ F89887FD16DAF59400B5ED12 = { isa = PBXGroup; children = ( - F898885016DB11AA00B5ED12 /* libsqlite3.dylib */, F898880B16DAF59400B5ED12 /* Mars */, F898882016DAF59500B5ED12 /* MarsTests */, F898880816DAF59400B5ED12 /* Frameworks */, @@ -128,7 +148,7 @@ isa = PBXGroup; children = ( F898880616DAF59400B5ED12 /* libMars.a */, - F898881716DAF59500B5ED12 /* MarsTests.octest */, + F898881716DAF59500B5ED12 /* MarsTests.xctest */, ); name = Products; sourceTree = ""; @@ -137,8 +157,6 @@ isa = PBXGroup; children = ( F898880916DAF59400B5ED12 /* Foundation.framework */, - F898881816DAF59500B5ED12 /* SenTestingKit.framework */, - F898881A16DAF59500B5ED12 /* UIKit.framework */, ); name = Frameworks; sourceTree = ""; @@ -241,6 +259,7 @@ F898880216DAF59400B5ED12 /* Sources */, F898880316DAF59400B5ED12 /* Frameworks */, F836F9CB16F01B55007DE438 /* Headers */, + 15256F7D1C29CB5A00E56634 /* CopyFiles */, ); buildRules = ( ); @@ -258,7 +277,6 @@ F898881216DAF59500B5ED12 /* Sources */, F898881316DAF59500B5ED12 /* Frameworks */, F898881416DAF59500B5ED12 /* Resources */, - F898881516DAF59500B5ED12 /* ShellScript */, ); buildRules = ( ); @@ -267,8 +285,8 @@ ); name = MarsTests; productName = MarsTests; - productReference = F898881716DAF59500B5ED12 /* MarsTests.octest */; - productType = "com.apple.product-type.bundle.ocunit-test"; + productReference = F898881716DAF59500B5ED12 /* MarsTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ @@ -276,7 +294,8 @@ F89887FE16DAF59400B5ED12 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0600; + LastTestingUpgradeCheck = 0720; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = "Central Atomics"; }; buildConfigurationList = F898880116DAF59400B5ED12 /* Build configuration list for PBXProject "Mars" */; @@ -308,22 +327,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - F898881516DAF59500B5ED12 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ F898880216DAF59400B5ED12 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -380,25 +383,35 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_PEDANTIC = NO; + GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 6.1; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; WARNING_CFLAGS = ""; @@ -412,18 +425,27 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_PEDANTIC = NO; + GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 6.1; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; WARNING_CFLAGS = ""; @@ -436,8 +458,11 @@ DSTROOT = /tmp/Mars.dst; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Mars/Mars-Prefix.pch"; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - OTHER_LDFLAGS = "-ObjC"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + OTHER_LDFLAGS = ( + "-ObjC", + "-lsqlite3", + ); PRIVATE_HEADERS_FOLDER_PATH = ../../include/Mars/Private; PRODUCT_NAME = "$(TARGET_NAME)"; PUBLIC_HEADERS_FOLDER_PATH = ../../include/Mars; @@ -451,8 +476,11 @@ DSTROOT = /tmp/Mars.dst; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Mars/Mars-Prefix.pch"; - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - OTHER_LDFLAGS = "-ObjC"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + OTHER_LDFLAGS = ( + "-ObjC", + "-lsqlite3", + ); PRIVATE_HEADERS_FOLDER_PATH = ../../include/Mars/Private; PRODUCT_NAME = "$(TARGET_NAME)"; PUBLIC_HEADERS_FOLDER_PATH = ../../include/Mars; @@ -463,32 +491,32 @@ F898882F16DAF59500B5ED12 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "\"$(SDKROOT)/Developer/Library/Frameworks\"", - "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Mars/Mars-Prefix.pch"; INFOPLIST_FILE = "MarsTests/MarsTests-Info.plist"; - OTHER_LDFLAGS = "-all_load"; + OTHER_LDFLAGS = ( + "-all_load", + "-lsqlite3", + ); + PRODUCT_BUNDLE_IDENTIFIER = "centralatomics.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; - WRAPPER_EXTENSION = octest; }; name = Debug; }; F898883016DAF59500B5ED12 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - FRAMEWORK_SEARCH_PATHS = ( - "\"$(SDKROOT)/Developer/Library/Frameworks\"", - "\"$(DEVELOPER_LIBRARY_DIR)/Frameworks\"", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Mars/Mars-Prefix.pch"; INFOPLIST_FILE = "MarsTests/MarsTests-Info.plist"; - OTHER_LDFLAGS = "-all_load"; + OTHER_LDFLAGS = ( + "-all_load", + "-lsqlite3", + ); + PRODUCT_BUNDLE_IDENTIFIER = "centralatomics.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; - WRAPPER_EXTENSION = octest; }; name = Release; }; diff --git a/Mars/Mars/MConnection.h b/Mars/Mars/MConnection.h index 14022ee..395d089 100644 --- a/Mars/Mars/MConnection.h +++ b/Mars/Mars/MConnection.h @@ -27,6 +27,7 @@ - (BOOL)exec:(NSString *)sql error:(NSError **)error; - (int64_t)executeUpdate:(MQuery *)query error:(NSError **)error; - (NSArray *)executeQuery:(MQuery *)query error:(NSError **)error; +- (id)executeRawQuery:(NSString *)rawQuery error:(NSError **)error; - (BOOL)beginTransaction:(NSError **)error; - (BOOL)commit:(NSError **)error; - (BOOL)rollback:(NSError **)error; diff --git a/Mars/Mars/MConnection.m b/Mars/Mars/MConnection.m index 31e8dd8..66b7a2a 100644 --- a/Mars/Mars/MConnection.m +++ b/Mars/Mars/MConnection.m @@ -11,269 +11,290 @@ #import "MQuery+Private.h" #import "MSelectQuery.h" +void myTraceFunc(void *uData, const char *statement) +{ + CTLog(@"TRACE: %s", statement); +} + @implementation MConnection { - sqlite3 *_dbHandle; - NSString *_dbPath; + sqlite3 *_dbHandle; + NSString *_dbPath; } - (id)init { - return [self initWithPath:nil]; + return [self initWithPath:nil]; } - (id)initWithPath:(NSString *)path { - self = [super init]; - if (self) { - _dbPath = path; - } - return self; + self = [super init]; + if (self) { + _dbPath = path; + } + return self; } - (BOOL)open { - int err = sqlite3_open_v2(_dbPath ? (char *)[_dbPath fileSystemRepresentation] : ":memory:", &_dbHandle, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_SHAREDCACHE, NULL); - - if (err != SQLITE_OK) { - CTLog(@"ERROR OPENING DB: %d", err); - return NO; - } - - [self configureDatabaseSettings]; - - return YES; + int err = sqlite3_open_v2(_dbPath ? (char *)[_dbPath fileSystemRepresentation] : ":memory:", &_dbHandle, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_SHAREDCACHE, NULL); + + if (err != SQLITE_OK) { + CTLog(@"ERROR OPENING DB: %d", err); + return NO; + } + + [self configureDatabaseSettings]; + + return YES; } - (void)close { - sqlite3_close(_dbHandle); - _dbHandle = NULL; + sqlite3_close(_dbHandle); + _dbHandle = NULL; } - (BOOL)exec:(NSString *)sql error:(NSError **)error { - const char *charSql = [sql cStringUsingEncoding:NSUTF8StringEncoding]; - char *errorPointer; - if (sqlite3_exec(_dbHandle, charSql, NULL, NULL, &errorPointer) != SQLITE_OK) { - CTLog(@"ERROR RUNNING %@: %s", sql, errorPointer); - if (error) { - *error = self.lastError; - } - sqlite3_free(errorPointer); - return NO; - } - return YES; + const char *charSql = [sql cStringUsingEncoding:NSUTF8StringEncoding]; + char *errorPointer; + if (sqlite3_exec(_dbHandle, charSql, NULL, NULL, &errorPointer) != SQLITE_OK) { + CTLog(@"ERROR RUNNING %@: %s", sql, errorPointer); + if (error) { + *error = self.lastError; + } + sqlite3_free(errorPointer); + return NO; + } + return YES; } - (int64_t)executeUpdate:(MQuery *)query error:(NSError **)error { - if ([query isKindOfClass:[MSelectQuery class]]) { - [NSException raise:@"Invalid Query Type" format:@"Only UPDATE, DELETE, INSERT queries are allowed"]; - } + if ([query isKindOfClass:[MSelectQuery class]]) { + [NSException raise:@"Invalid Query Type" format:@"Only UPDATE, DELETE, INSERT queries are allowed"]; + } #if LOG_SQL - NSLog(@"%@", query.sql); + NSLog(@"%@", query.sql); #endif - sqlite3_stmt *stmt = [self createStatement:query.sql bindings:query.bindings error:error]; - if (!stmt) { - return kNoPk; - } - int64_t row = [self executeUpdateWithStatement:stmt error:error]; - [self finalizeStatement:stmt]; - return row; + sqlite3_stmt *stmt = [self createStatement:query.sql bindings:query.bindings error:error]; + if (!stmt) { + return kNoPk; + } + int64_t row = [self executeUpdateWithStatement:stmt error:error]; + [self finalizeStatement:stmt]; + return row; } - (NSArray *)executeQuery:(MQuery *)query error:(NSError **)error { - if (![query isKindOfClass:[MSelectQuery class]]) { - [NSException raise:@"Invalid Query Type" format:@"Only SELECT queries are allowed"]; - } + if (![query isKindOfClass:[MSelectQuery class]]) { + [NSException raise:@"Invalid Query Type" format:@"Only SELECT queries are allowed"]; + } #if LOG_SQL - NSLog(@"%@", query.sql); + NSLog(@"%@", query.sql); #endif - sqlite3_stmt *stmt = [self createStatement:query.sql bindings:query.bindings error:error]; - if (!stmt) { - return nil; - } - NSArray *results = [self executeQueryWithStatement:stmt error:error]; - [self finalizeStatement:stmt]; - return results; + sqlite3_stmt *stmt = [self createStatement:query.sql bindings:query.bindings error:error]; + if (!stmt) { + return nil; + } + NSArray *results = [self executeQueryWithStatement:stmt error:error]; + [self finalizeStatement:stmt]; + return results; } - (BOOL)beginTransaction:(NSError **)error { - return [self exec:@"BEGIN TRANSACTION" error:error]; + return [self exec:@"BEGIN TRANSACTION" error:error]; } - (BOOL)commit:(NSError **)error { - return [self exec:@"COMMIT" error:error]; + return [self exec:@"COMMIT" error:error]; } - (BOOL)rollback:(NSError **)error { - return [self exec:@"ROLLBACK" error:error]; + return [self exec:@"ROLLBACK" error:error]; } - (sqlite3 *)dbHandle { - return _dbHandle; + return _dbHandle; } - (int64_t)lastInsertRowId { - return sqlite3_last_insert_rowid(_dbHandle); + return sqlite3_last_insert_rowid(_dbHandle); } - (NSError *)lastError { - NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; - [errorDetail setValue:[self lastErrorMessage] forKey:NSLocalizedDescriptionKey]; - return [NSError errorWithDomain:@"MDatabase" code:[self lastErrorCode] userInfo:errorDetail]; + NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; + [errorDetail setValue:[self lastErrorMessage] forKey:NSLocalizedDescriptionKey]; + return [NSError errorWithDomain:@"MDatabase" code:[self lastErrorCode] userInfo:errorDetail]; } - (NSString *)lastErrorMessage { - return [NSString stringWithUTF8String:sqlite3_errmsg(_dbHandle)]; + return [NSString stringWithUTF8String:sqlite3_errmsg(_dbHandle)]; } - (int)lastErrorCode { - return sqlite3_errcode(_dbHandle); + return sqlite3_errcode(_dbHandle); } - (void)configureDatabaseSettings { - [self exec:@"PRAGMA foreign_keys = ON;" error:nil]; - [self exec:@"PRAGMA synchronous = OFF;" error:nil]; - [self exec:@"PRAGMA journal_mode = WAL;" error:nil]; + [self exec:@"PRAGMA foreign_keys = ON;" error:nil]; + [self exec:@"PRAGMA synchronous = OFF;" error:nil]; + [self exec:@"PRAGMA journal_mode = WAL;" error:nil]; } - (NSArray *)executeQueryWithStatement:(sqlite3_stmt *)stmt error:(NSError **)error { - int r = 0; - NSMutableArray *results = [NSMutableArray array]; - NSArray *columnNames = [self columnsForStatement:stmt]; - - while ((r = sqlite3_step(stmt)) != SQLITE_DONE) { - if (r == SQLITE_ROW) { - NSDictionary *columns = [NSMutableDictionary dictionary]; - int i = 0; - for (NSString *columnName in columnNames) { - [columns setValue:[self valueForColumn:i query:stmt] forKey:columnName]; - ++i; - } - [results addObject:columns]; - } else { - CTLog(@"Error calling sqlite3_step %@", self.lastError); - if (error) { - *error = self.lastError; - } - } - } - return results; + int r = 0; + NSMutableArray *results = [NSMutableArray array]; + NSArray *columnNames = [self columnsForStatement:stmt]; + + while ((r = sqlite3_step(stmt)) != SQLITE_DONE) { + if (r == SQLITE_ROW) { + NSDictionary *columns = [NSMutableDictionary dictionary]; + int i = 0; + for (NSString *columnName in columnNames) { + [columns setValue:[self valueForColumn:i query:stmt] forKey:columnName]; + ++i; + } + [results addObject:columns]; + } else { + CTLog(@"Error %li calling sqlite3_step exec_query %@", r, self.lastError); +// sqlite3_trace(self.dbHandle, myTraceFunc, NULL); + if (error) { + *error = self.lastError; + } + } + } + return results; +} + +- (id)executeRawQuery:(NSString *)rawQuery error:(NSError **)error +{ + sqlite3_stmt *stmt = [self createStatement:rawQuery bindings:@[] error:error]; + if (!stmt) { + *error = self.lastError; + CTLog(@"Error creating statement for raw query %@", self.lastError); + return nil; + } + NSArray *results = [self executeQueryWithStatement:stmt error:error]; + [self finalizeStatement:stmt]; + + return results; } - (BOOL)executeUpdateWithStatement:(sqlite3_stmt *)stmt error:(NSError **)error { - int rc = sqlite3_step(stmt); - - if (SQLITE_DONE == rc) { - return YES; - } else if (rc == SQLITE_ROW) { - NSAssert(NO, @"A executeUpdate is being called with a query string"); - } else { - CTLog(@"Error calling sqlite3_step %@", self.lastError); - if (error) { - *error = self.lastError; - } - return NO; - } - - return NO; + int rc = sqlite3_step(stmt); + + if (SQLITE_DONE == rc) { + return YES; + } else if (rc == SQLITE_ROW) { + NSAssert(NO, @"A executeUpdate is being called with a query string"); + } else { + CTLog(@"Error %li calling sqlite3_step exec_update_query %@", rc, self.lastError); +// sqlite3_trace(self.dbHandle, myTraceFunc, NULL); + if (error) { + *error = self.lastError; + } + return NO; + } + + return NO; } // Taken from FMDB - (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt *)pStmt { - if ((!obj) || ((NSNull *)obj == [NSNull null])) { - sqlite3_bind_null(pStmt, idx); - } else if ([obj isKindOfClass:[NSData class]]) { - const void *bytes = [obj bytes]; - if (!bytes) { - // it's an empty NSData object, aka [NSData data]. - // Don't pass a NULL pointer, or sqlite will bind a SQL null instead of a blob. - bytes = ""; - } - sqlite3_bind_blob(pStmt, idx, bytes, (int)[obj length], SQLITE_STATIC); - } else if ([obj isKindOfClass:[NSDate class]]) { - sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]); - } else if ([obj isKindOfClass:[NSNumber class]]) { - if (strcmp([obj objCType], @encode(BOOL)) == 0) { - sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0)); - } else if (strcmp([obj objCType], @encode(int)) == 0) { - sqlite3_bind_int64(pStmt, idx, [obj longValue]); - } else if (strcmp([obj objCType], @encode(long)) == 0) { - sqlite3_bind_int64(pStmt, idx, [obj longValue]); - } else if (strcmp([obj objCType], @encode(long long)) == 0) { - sqlite3_bind_int64(pStmt, idx, [obj longLongValue]); - } else if (strcmp([obj objCType], @encode(unsigned long long)) == 0) { - sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedLongLongValue]); - } else if (strcmp([obj objCType], @encode(float)) == 0) { - sqlite3_bind_double(pStmt, idx, [obj floatValue]); - } else if (strcmp([obj objCType], @encode(double)) == 0) { - sqlite3_bind_double(pStmt, idx, [obj doubleValue]); - } else { - sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC); - } - } else { - sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC); - } + if ((!obj) || ((NSNull *)obj == [NSNull null])) { + sqlite3_bind_null(pStmt, idx); + } else if ([obj isKindOfClass:[NSData class]]) { + const void *bytes = [obj bytes]; + if (!bytes) { + // it's an empty NSData object, aka [NSData data]. + // Don't pass a NULL pointer, or sqlite will bind a SQL null instead of a blob. + bytes = ""; + } + sqlite3_bind_blob(pStmt, idx, bytes, (int)[obj length], SQLITE_STATIC); + } else if ([obj isKindOfClass:[NSDate class]]) { + sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]); + } else if ([obj isKindOfClass:[NSNumber class]]) { + if (strcmp([obj objCType], @encode(BOOL)) == 0) { + sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0)); + } else if (strcmp([obj objCType], @encode(int)) == 0) { + sqlite3_bind_int64(pStmt, idx, [obj longValue]); + } else if (strcmp([obj objCType], @encode(long)) == 0) { + sqlite3_bind_int64(pStmt, idx, [obj longValue]); + } else if (strcmp([obj objCType], @encode(long long)) == 0) { + sqlite3_bind_int64(pStmt, idx, [obj longLongValue]); + } else if (strcmp([obj objCType], @encode(unsigned long long)) == 0) { + sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedLongLongValue]); + } else if (strcmp([obj objCType], @encode(float)) == 0) { + sqlite3_bind_double(pStmt, idx, [obj floatValue]); + } else if (strcmp([obj objCType], @encode(double)) == 0) { + sqlite3_bind_double(pStmt, idx, [obj doubleValue]); + } else { + sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC); + } + } else { + sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC); + } } // Take from DatabaseKit - (id)valueForColumn:(unsigned int)colIndex query:(sqlite3_stmt *)query { - int columnType = sqlite3_column_type(query, colIndex); - switch (columnType) { - case SQLITE_INTEGER: - return @(sqlite3_column_int(query, colIndex)); - break; - case SQLITE_FLOAT: - return @(sqlite3_column_double(query, colIndex)); - break; - case SQLITE_BLOB: - return [NSData dataWithBytes:sqlite3_column_blob(query, colIndex) - length:sqlite3_column_bytes(query, colIndex)]; - break; - case SQLITE_NULL: - return [NSNull null]; - break; - case SQLITE_TEXT: - return @((const char *)sqlite3_column_text(query, colIndex)); - break; - default: - // It really shouldn't ever come to this. - break; - } - return nil; + int columnType = sqlite3_column_type(query, colIndex); + switch (columnType) { + case SQLITE_INTEGER: + return @(sqlite3_column_int(query, colIndex)); + break; + case SQLITE_FLOAT: + return @(sqlite3_column_double(query, colIndex)); + break; + case SQLITE_BLOB: + return [NSData dataWithBytes:sqlite3_column_blob(query, colIndex) + length:sqlite3_column_bytes(query, colIndex)]; + break; + case SQLITE_NULL: + return [NSNull null]; + break; + case SQLITE_TEXT: + return @((const char *)sqlite3_column_text(query, colIndex)); + break; + default: + // It really shouldn't ever come to this. + break; + } + return nil; } // Taken from DatabaseKit - (NSArray *)columnsForStatement:(sqlite3_stmt *)query { - int columnCount = sqlite3_column_count(query); - if (columnCount <= 0) { - return nil; - } - - NSMutableArray *columnNames = [NSMutableArray array]; - for (int i = 0; i < columnCount; ++i) { - const char *name; - name = sqlite3_column_name(query, i); - [columnNames addObject:@(name)]; - } - return columnNames; + int columnCount = sqlite3_column_count(query); + if (columnCount <= 0) { + return nil; + } + + NSMutableArray *columnNames = [NSMutableArray array]; + for (int i = 0; i < columnCount; ++i) { + const char *name; + name = sqlite3_column_name(query, i); + [columnNames addObject:@(name)]; + } + return columnNames; } - (sqlite3_stmt *)createStatement:(NSString *)sql bindings:(NSArray *)bindings error:(NSError **)error { - sqlite3_stmt *stmt; - int rc = sqlite3_prepare_v2(self.dbHandle, [sql UTF8String], -1, &stmt, 0); - if (SQLITE_OK != rc) { - if (error) *error = self.lastError; - CTLog(@"Error preparing statement: %@ ", sql, self.lastError); - sqlite3_finalize(stmt); - return NULL; - } - - for (int i = 0; i < bindings.count; i++) { - id value = [bindings objectAtIndex:i]; - [self bindObject:value toColumn:i+1 inStatement:stmt]; - } - return stmt; + sqlite3_stmt *stmt; + int rc = sqlite3_prepare_v2(self.dbHandle, [sql UTF8String], -1, &stmt, 0); + if (SQLITE_OK != rc) { + if (error) *error = self.lastError; + CTLog(@"Error preparing statement: %@ ", sql, self.lastError); + sqlite3_finalize(stmt); + return NULL; + } + + for (int i = 0; i < bindings.count; i++) { + id value = [bindings objectAtIndex:i]; + [self bindObject:value toColumn:i+1 inStatement:stmt]; + } + return stmt; } - (void)finalizeStatement:(sqlite3_stmt *)stmt { - sqlite3_finalize(stmt); + sqlite3_finalize(stmt); } diff --git a/Mars/Mars/MDatabase+AsyncAdditions.m b/Mars/Mars/MDatabase+AsyncAdditions.m index dff5532..b41ab89 100644 --- a/Mars/Mars/MDatabase+AsyncAdditions.m +++ b/Mars/Mars/MDatabase+AsyncAdditions.m @@ -13,7 +13,11 @@ @implementation MDatabase (AsyncAdditions) - (void)queries:(NSArray *)queries completionBlock:(void (^)(NSError *err, NSArray *results))completionBlock { __block int finished = 0; NSMutableArray *results = [NSMutableArray array]; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused" for (MQuery *query in queries) { +#pragma clang diagnostic pop [results addObject:[NSNull null]]; // Acts as a placeholder } diff --git a/Mars/Mars/MDatabase.h b/Mars/Mars/MDatabase.h index dc5ff1f..b0c0e14 100644 --- a/Mars/Mars/MDatabase.h +++ b/Mars/Mars/MDatabase.h @@ -22,9 +22,16 @@ - (id)initWithPath:(NSString *)path schema:(NSString *)schema; - (id)initWithDBFileName:(NSString *)dbFileName schemaFileName:(NSString *)schemaFileName; -// Non blocking version +// Non blocking versions - (NSOperation *)query:(MQuery *)query completionBlock:(void (^)(NSError *err, id result))completionBlock; +- (NSOperation *)query:(MQuery *)query +withCompletionOnMainThread:(BOOL)completionOnMainThread + completionBlock:(void (^)(NSError *err, id result))completionBlock; + // Blocking version - (id)query:(MQuery *)query error:(NSError **)err; + +- (id)rawQuery:(NSString *)query error:(NSError **)error; +- (NSOperation *)rawQuery:(NSString *)query completionBlock:(void (^)(NSError *err, id result))completionBlock; @end diff --git a/Mars/Mars/MDatabase.m b/Mars/Mars/MDatabase.m index 1b7d972..acfa951 100644 --- a/Mars/Mars/MDatabase.m +++ b/Mars/Mars/MDatabase.m @@ -68,17 +68,35 @@ - (id)initWithDBFileName:(NSString *)dbFileName schemaFileName:(NSString *)schem } - (NSOperation *)query:(MQuery *)query completionBlock:(void (^)(NSError *err, id result))completionBlock { + return [self query:query withCompletionOnMainThread:YES completionBlock:completionBlock]; +} + +- (NSOperation *)query:(MQuery *)query +withCompletionOnMainThread:(BOOL)completionOnMainThread + completionBlock:(void (^)(NSError *err, id result))completionBlock { if ([query modifies]) { return [self change:query completionBlock:^(NSError *err, id result) { - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - if (completionBlock) completionBlock(err, result); - }]; + if (completionBlock) { + if (completionOnMainThread) { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + completionBlock(err, result); + }]; + } else { + completionBlock(err, result); + } + } }]; } else { return [self select:query completionBlock:^(NSError *err, id result) { - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - if (completionBlock) completionBlock(err, result); - }]; + if (completionBlock) { + if (completionOnMainThread) { + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + completionBlock(err, result); + }]; + } else { + completionBlock(err, result); + } + } }]; } } @@ -110,6 +128,48 @@ - (id)query:(MQuery *)query error:(NSError **)err { } } +- (id)rawQuery:(NSString *)query error:(NSError *__autoreleasing *)error +{ + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block NSError *err = nil; + __block id result = nil; + + [self rawQuery:query completionBlock:^(NSError *e, id r) { + + result = r; + err = e; + dispatch_semaphore_signal(semaphore); + }]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + if (error) *error = err; + if (err) { + return nil; + } else { + return result; + } +} + +- (NSOperation *)rawQuery:(NSString *)query completionBlock:(void (^)(NSError *err, id result))completionBlock +{ + __weak MDatabase *weakSelf = self; + NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ + MDatabase *strongSelf = weakSelf; + MConnection *reader = [strongSelf reader]; + NSError *error = nil; + NSArray *val = [reader executeRawQuery:query error:&error]; + if (val) { + if (completionBlock) completionBlock(nil, val); + + } else { + if (completionBlock) completionBlock(error, nil); + + } + [self putBackReader:reader]; + }]; + [self.readQueue addOperation:op]; + return op; +} + - (NSOperation *)select:(MQuery *)query completionBlock:(void (^)(NSError *err, id result))completionBlock { __weak MDatabase *weakSelf = self; NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ diff --git a/Mars/Mars/MSelectQuery.h b/Mars/Mars/MSelectQuery.h index a7eab62..09d28d8 100644 --- a/Mars/Mars/MSelectQuery.h +++ b/Mars/Mars/MSelectQuery.h @@ -12,9 +12,11 @@ @interface MSelectQuery : MQuery - (instancetype)where:(NSDictionary *)expressions; +- (instancetype)whereRawSql:(NSString *)rawExpression args:(NSArray *)args; - (instancetype)orderByAsc:(NSString *)field; - (instancetype)orderByDesc:(NSString *)field; - (instancetype)limit:(NSUInteger)limit; - (instancetype)limit:(NSUInteger)limit offset:(NSUInteger)offset; - (instancetype)join:(NSString *)join; +- (instancetype)groupBy:(NSString *)group; @end diff --git a/Mars/Mars/MSelectQuery.m b/Mars/Mars/MSelectQuery.m index 8cec84e..7ca1f88 100644 --- a/Mars/Mars/MSelectQuery.m +++ b/Mars/Mars/MSelectQuery.m @@ -15,6 +15,9 @@ @interface MSelectQuery () @property (nonatomic, assign) NSUInteger limit; @property (nonatomic, assign) NSUInteger offset; @property (nonatomic, strong) NSString *join; +@property (nonatomic, strong) NSString *groupBy; +@property (nonatomic, strong) NSArray *whereRawArgs; +@property (nonatomic, strong) NSString *whereRawExpr; @end @implementation MSelectQuery @@ -29,6 +32,9 @@ - (id)copyWithZone:(NSZone *)zone { query.join = self.join; query.order = self.order; query.orderBy = self.orderBy; + query.groupBy = self.groupBy; + query.whereRawArgs = self.whereRawArgs; + query.whereRawExpr = self.whereRawExpr; return query; } @@ -43,7 +49,7 @@ - (NSString *)sql { } NSMutableString *str = nil; - if (self.where || self.join) { + if (self.where || self.whereRawExpr || self.join) { str = [NSMutableString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@", rowStr, [self tableString], [self whereString]]; } else { str = [NSMutableString stringWithFormat:@"SELECT %@ FROM %@", rowStr, [self tableString]]; @@ -71,8 +77,14 @@ - (NSString *)tableString { } - (NSString *)whereString { - NSMutableString *where = [[super whereString] mutableCopy]; - if (self.join && where.length > 0) { + NSString *whereString; + if (self.whereRawExpr) { + whereString = self.whereRawExpr; + } else { + whereString = [super whereString]; + } + NSMutableString *where = [whereString mutableCopy]; + if (self.join && where.length > 0) { [where appendFormat:@" AND %@", self.join]; } else if (self.join && where.length == 0) { return self.join; @@ -113,11 +125,25 @@ - (NSString *)asString:(NSString *)table alias:(NSString *)alias { return [NSString stringWithFormat:@"%@ AS %@", [self quote:table], [self quote:alias]]; } +- (NSArray *)bindings { + if (self.whereRawExpr) { + return self.whereRawArgs; + } + return [super bindings]; +} + // Have to do this to get the compiler to stop complaining - (instancetype)where:(NSDictionary *)expressions { return (MSelectQuery *)[super where:expressions]; } +- (instancetype)whereRawSql:(NSString *)rawExpression args:(NSArray *)args { + MSelectQuery *query = [self copy]; + query.whereRawArgs = args; + query.whereRawExpr = rawExpression; + return query; +} + - (instancetype)orderByAsc:(NSString *)field { MSelectQuery *query = [self copy]; query.orderBy = field; @@ -138,6 +164,14 @@ - (instancetype)limit:(NSUInteger)limit { return query; } +- (instancetype)groupBy:(NSString *)group +{ + MSelectQuery *query = [self copy]; + query.groupBy = group; + + return query; +} + - (instancetype)limit:(NSUInteger)limit offset:(NSUInteger)offset { MSelectQuery *query = [self copy]; query.limit = limit; diff --git a/Mars/MarsTests/MarsTests-Info.plist b/Mars/MarsTests/MarsTests-Info.plist index ccca2ea..169b6f7 100644 --- a/Mars/MarsTests/MarsTests-Info.plist +++ b/Mars/MarsTests/MarsTests-Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - centralatomics.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType diff --git a/Mars/MarsTests/MarsTests.h b/Mars/MarsTests/MarsTests.h index d98af9e..ddf7723 100644 --- a/Mars/MarsTests/MarsTests.h +++ b/Mars/MarsTests/MarsTests.h @@ -6,8 +6,8 @@ // Copyright (c) 2013 Central Atomics. All rights reserved. // -#import +#import -@interface MarsTests : SenTestCase +@interface MarsTests : XCTestCase @end diff --git a/Mars/MarsTests/MarsTests.m b/Mars/MarsTests/MarsTests.m index 6e8a009..412b168 100644 --- a/Mars/MarsTests/MarsTests.m +++ b/Mars/MarsTests/MarsTests.m @@ -27,8 +27,8 @@ - (void)setUp { MQuery *insert = [MQuery insertInto:@"emails" values:@{@"name":@"Matt", @"email":@"matt@gmail.com", @"count":@(3)}]; int64_t r = [_conn executeUpdate:insert error:nil]; - STAssertTrue(r > 0, nil); - STAssertTrue([_conn lastInsertRowId] == 1, nil); + XCTAssertTrue(r > 0); + XCTAssertTrue([_conn lastInsertRowId] == 1); } - (void)tearDown { @@ -40,42 +40,42 @@ - (void)tearDown { - (void)testSelect { MQuery *query = [MQuery selectFrom:@"emails"]; NSArray *results = [_conn executeQuery:query error:nil]; - STAssertTrue(results.count > 0, nil); - STAssertEqualObjects(@"Matt", results[0][@"name"], nil); + XCTAssertTrue(results.count > 0); + XCTAssertEqualObjects(@"Matt", results[0][@"name"]); } - (void)testSelectCount { - STAssertTrue(1 == self.emailCount, nil); + XCTAssertTrue(1 == self.emailCount); } - (void)testDeleteCount { int64_t r = [_conn executeUpdate:self.deletionQuery error:nil]; - STAssertTrue(r > 0, nil); - STAssertTrue(0 == self.emailCount, nil); + XCTAssertTrue(r > 0); + XCTAssertTrue(0 == self.emailCount); } - (void)testRollbackTransactions { BOOL success = [_conn beginTransaction:nil]; - STAssertTrue(success, nil); + XCTAssertTrue(success); int64_t r = [_conn executeUpdate:self.deletionQuery error:nil]; - STAssertTrue(r > 0, nil); + XCTAssertTrue(r > 0); success = [_conn rollback:nil]; - STAssertTrue(success, nil); - STAssertTrue(1 == self.emailCount, @"Query was rolled back shouldn't have changed"); + XCTAssertTrue(success); + XCTAssertTrue(1 == self.emailCount, @"Query was rolled back shouldn't have changed"); } - (void)testCommitTransaction { BOOL success = [_conn beginTransaction:nil]; - STAssertTrue(success, nil); + XCTAssertTrue(success); int64_t r = [_conn executeUpdate:self.deletionQuery error:nil]; - STAssertTrue(r > 0, nil); + XCTAssertTrue(r > 0); success = [_conn commit:nil]; - STAssertTrue(success, nil); - STAssertTrue(0 == self.emailCount, nil); + XCTAssertTrue(success); + XCTAssertTrue(0 == self.emailCount); } - (MQuery *)deletionQuery { @@ -85,7 +85,7 @@ - (MQuery *)deletionQuery { - (int)emailCount { MQuery *query = [MQuery select:@"COUNT(*)" from:@"emails"]; NSArray *results = [_conn executeQuery:query error:nil]; - STAssertTrue(results.count > 0, nil); + XCTAssertTrue(results.count > 0); return [results[0][@"COUNT(*)"] intValue]; } @end diff --git a/Mars/MarsTests/QueryTests.h b/Mars/MarsTests/QueryTests.h index e69ced1..283b3c1 100644 --- a/Mars/MarsTests/QueryTests.h +++ b/Mars/MarsTests/QueryTests.h @@ -6,8 +6,8 @@ // Copyright (c) 2013 Central Atomics. All rights reserved. // -#import +#import -@interface QueryTests : SenTestCase +@interface QueryTests : XCTestCase @end diff --git a/Mars/MarsTests/QueryTests.m b/Mars/MarsTests/QueryTests.m index 9e51167..a4f7ba1 100644 --- a/Mars/MarsTests/QueryTests.m +++ b/Mars/MarsTests/QueryTests.m @@ -19,110 +19,121 @@ - (void)testSelectQuery { MSelectQuery *query = nil; query = [MQuery selectFrom:@"emails"]; - STAssertEqualObjects([query sql], @"SELECT * FROM \"emails\"", nil); + XCTAssertEqualObjects([query sql], @"SELECT * FROM \"emails\""); query = [MQuery select:@"name" from:@"emails"]; - STAssertEqualObjects([query sql], @"SELECT \"name\" FROM \"emails\"", nil); + XCTAssertEqualObjects([query sql], @"SELECT \"name\" FROM \"emails\""); query = [MQuery select:@"emails.name" from:@"emails"]; - STAssertEqualObjects([query sql], @"SELECT \"emails\".\"name\" FROM \"emails\"", nil); + XCTAssertEqualObjects([query sql], @"SELECT \"emails\".\"name\" FROM \"emails\""); query = [MQuery select:@[@"name", @"to", @"from"] from:@"emails"]; - STAssertEqualObjects([query sql], @"SELECT \"name\", \"to\", \"from\" FROM \"emails\"", nil); + XCTAssertEqualObjects([query sql], @"SELECT \"name\", \"to\", \"from\" FROM \"emails\""); query = [[MQuery selectFrom:@"emails"] where:@{@"to":@"matt", @"count":@(3)}]; - STAssertEqualObjects(@"SELECT * FROM \"emails\" WHERE \"count\"=? AND \"to\"=?", [query sql], nil); + XCTAssertEqualObjects(@"SELECT * FROM \"emails\" WHERE \"count\"=? AND \"to\"=?", [query sql]); NSArray *correctBindings = @[@(3), @"matt"]; - STAssertEqualObjects([query bindings], correctBindings, nil); + XCTAssertEqualObjects([query bindings], correctBindings); } - (void)testSelectWhereInQuery { MSelectQuery *query = [[MQuery selectFrom:@"emails"] where:@{@"to":@"matt", @"count":@(3), @"uid":@[@(1), @(2)]}]; - STAssertEqualObjects(@"SELECT * FROM \"emails\" WHERE \"count\"=? AND \"to\"=? AND \"uid\" IN (?,?)", [query sql], nil); + XCTAssertEqualObjects(@"SELECT * FROM \"emails\" WHERE \"count\"=? AND \"to\"=? AND \"uid\" IN (?,?)", [query sql]); NSArray *correctBindings = @[@(3), @"matt", @(1), @(2)]; - STAssertEqualObjects([query bindings], correctBindings, nil); + XCTAssertEqualObjects([query bindings], correctBindings); +} + +- (void)testSelectWhereRawSqlQuery { + MSelectQuery *query = [[MQuery select:@[@"email"] from:@[@[@"accounts", @"a"]]] whereRawSql:@"a.email LIKE ?" args:@[@"test"]]; + XCTAssertEqualObjects([query sql], @"SELECT \"email\" FROM \"accounts\" AS \"a\" WHERE a.email LIKE ?"); +} + +- (void)testSelectWhereRawSqlJoinQuery { + NSArray *tables = @[@[@"emails", @"e"], @[@"address", @"a"]]; + MQuery *query = [[[MQuery select:@[@"e.name", @"a.location"] from:tables] whereRawSql:@"e.to LIKE ?" args:@[@"matt%"]] join:@"a.id=e.address"]; + XCTAssertEqualObjects([query sql], @"SELECT \"e\".\"name\", \"a\".\"location\" FROM \"emails\" AS \"e\", \"address\" AS \"a\" WHERE e.to LIKE ? AND a.id=e.address"); } - (void)testSelectAsQuery { MSelectQuery *query = nil; query = [MQuery select:@[@[@"name", @"n"], @"to", @[@"from", @"f"]] from:@"emails"]; - STAssertEqualObjects([query sql], @"SELECT \"name\" AS \"n\", \"to\", \"from\" AS \"f\" FROM \"emails\"", nil); + XCTAssertEqualObjects([query sql], @"SELECT \"name\" AS \"n\", \"to\", \"from\" AS \"f\" FROM \"emails\""); } - (void)testInsertQuery { MInsertQuery *query = nil; query = [MQuery insertInto:@"emails" values:@{@"to":@"matt", @"count":@(3)}]; - STAssertEqualObjects(@"INSERT INTO \"emails\" (\"count\", \"to\") VALUES (?, ?)", [query sql], nil); + XCTAssertEqualObjects(@"INSERT INTO \"emails\" (\"count\", \"to\") VALUES (?, ?)", [query sql]); NSArray *correctBindings = @[@(3), @"matt"]; - STAssertEqualObjects([query bindings], correctBindings, nil); + XCTAssertEqualObjects([query bindings], correctBindings); } - (void)testUpdateQuery { MUpdateQuery *query = nil; query = [MQuery update:@"emails" values:@{@"count":@(8), @"from":@"Bear"}]; - STAssertEqualObjects(@"UPDATE \"emails\" SET \"count\"=?, \"from\"=?", [query sql], nil); + XCTAssertEqualObjects(@"UPDATE \"emails\" SET \"count\"=?, \"from\"=?", [query sql]); NSArray *correctBindings = @[@(8), @"Bear"]; - STAssertEqualObjects([query bindings], correctBindings, nil); + XCTAssertEqualObjects([query bindings], correctBindings); query = [[MQuery update:@"emails" values:@{@"count":@(2), @"from":@"bear"}] where:@{@"to":@"matt"}]; - STAssertEqualObjects(@"UPDATE \"emails\" SET \"count\"=?, \"from\"=? WHERE \"to\"=?", [query sql], nil); + XCTAssertEqualObjects(@"UPDATE \"emails\" SET \"count\"=?, \"from\"=? WHERE \"to\"=?", [query sql]); correctBindings = @[@(2), @"bear", @"matt"]; - STAssertEqualObjects([query bindings], correctBindings, nil); + XCTAssertEqualObjects([query bindings], correctBindings); } - (void)testDeleteQuery { MDeleteQuery *query = nil; query = [MQuery deleteFrom:@"emails"]; - STAssertEqualObjects(@"DELETE FROM \"emails\"", [query sql], nil); + XCTAssertEqualObjects(@"DELETE FROM \"emails\"", [query sql]); query = [[MQuery deleteFrom:@"emails"] where:@{@"to":@"matt"}]; - STAssertEqualObjects(@"DELETE FROM \"emails\" WHERE \"to\"=?", [query sql], nil); + XCTAssertEqualObjects(@"DELETE FROM \"emails\" WHERE \"to\"=?", [query sql]); NSArray *correctBindings = @[@"matt"]; - STAssertEqualObjects([query bindings], correctBindings, nil); + XCTAssertEqualObjects([query bindings], correctBindings); } - (void)testTableFormats { MSelectQuery *query = nil; query = [MQuery selectFrom:@"emails"]; - STAssertEqualObjects([query sql], @"SELECT * FROM \"emails\"", nil); + XCTAssertEqualObjects([query sql], @"SELECT * FROM \"emails\""); query = [MQuery selectFrom:@[@[@"emails", @"e"]]]; - STAssertEqualObjects([query sql], @"SELECT * FROM \"emails\" AS \"e\"", nil); + XCTAssertEqualObjects([query sql], @"SELECT * FROM \"emails\" AS \"e\""); query = [MQuery selectFrom:@[@[@"emails", @"e"], @[@"address", @"a"]]]; - STAssertEqualObjects([query sql], @"SELECT * FROM \"emails\" AS \"e\", \"address\" AS \"a\"", nil); + XCTAssertEqualObjects([query sql], @"SELECT * FROM \"emails\" AS \"e\", \"address\" AS \"a\""); } - (void)testLimit { MSelectQuery *query = nil; query = [[MQuery selectFrom:@"emails"] limit:50]; - STAssertEqualObjects([query sql], @"SELECT * FROM \"emails\" LIMIT 50", nil); + XCTAssertEqualObjects([query sql], @"SELECT * FROM \"emails\" LIMIT 50"); query = [[MQuery selectFrom:@"emails"] limit:50 offset:10]; - STAssertEqualObjects([query sql], @"SELECT * FROM \"emails\" LIMIT 50 OFFSET 10", nil); + XCTAssertEqualObjects([query sql], @"SELECT * FROM \"emails\" LIMIT 50 OFFSET 10"); } - (void)testJoin { NSArray *tables = @[@[@"emails", @"e"], @[@"address", @"a"]]; MQuery *query = [[[MQuery select:@[@"e.name", @"a.location"] from:tables] where:@{@"to" : @"matt"}] join:@"a.id=e.address"]; - STAssertEqualObjects(@"SELECT \"e\".\"name\", \"a\".\"location\" FROM \"emails\" AS \"e\", \"address\" AS \"a\" WHERE \"to\"=? AND a.id=e.address", [query sql], nil); + XCTAssertEqualObjects(@"SELECT \"e\".\"name\", \"a\".\"location\" FROM \"emails\" AS \"e\", \"address\" AS \"a\" WHERE \"to\"=? AND a.id=e.address", [query sql]); query = [[MQuery select:@[@"e.name", @"a.location"] from:tables] join:@"a.id=e.address"]; - STAssertEqualObjects(@"SELECT \"e\".\"name\", \"a\".\"location\" FROM \"emails\" AS \"e\", \"address\" AS \"a\" WHERE a.id=e.address", [query sql], nil); + XCTAssertEqualObjects(@"SELECT \"e\".\"name\", \"a\".\"location\" FROM \"emails\" AS \"e\", \"address\" AS \"a\" WHERE a.id=e.address", [query sql]); } - (void)testOrderBy { MSelectQuery *query = nil; query = [[MQuery selectFrom:@"emails"] orderByAsc:@"date"]; - STAssertEqualObjects([query sql], @"SELECT * FROM \"emails\" ORDER BY \"date\" ASC", nil); + XCTAssertEqualObjects([query sql], @"SELECT * FROM \"emails\" ORDER BY \"date\" ASC"); query = [[MQuery selectFrom:@"emails"] orderByDesc:@"date"]; - STAssertEqualObjects([query sql], @"SELECT * FROM \"emails\" ORDER BY \"date\" DESC", nil); + XCTAssertEqualObjects([query sql], @"SELECT * FROM \"emails\" ORDER BY \"date\" DESC"); } @end diff --git a/Mars/Supporting/CTLogger.m b/Mars/Supporting/CTLogger.m index 50b7205..44b4860 100644 --- a/Mars/Supporting/CTLogger.m +++ b/Mars/Supporting/CTLogger.m @@ -8,6 +8,7 @@ @implementation CTLogger { dispatch_queue_t queue; NSFileHandle *log; + NSDateFormatter *formatter; } + (id)logger { @@ -43,6 +44,9 @@ - (id)init { log = [NSFileHandle fileHandleForWritingAtPath:logPath]; [log seekToEndOfFile]; + formatter = [[NSDateFormatter alloc] init]; + [formatter setTimeZone:[NSTimeZone systemTimeZone]]; + [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ssssZ"]; } return self; } @@ -51,7 +55,7 @@ - (void)log:(NSString *)format, ... { va_list args; va_start(args, format); NSString *msg = [[NSString alloc] initWithFormat:format arguments:args]; - NSString *line = [NSString stringWithFormat:@"[%@] %@\n", [NSDate date], msg]; + NSString *line = [NSString stringWithFormat:@"[%@] %@\n", [formatter stringFromDate:[NSDate date]], msg]; va_end(args); dispatch_sync(queue, ^() { [log writeData:[line dataUsingEncoding:NSUTF8StringEncoding]]; @@ -70,4 +74,4 @@ - (NSString *)logFilePath { - (void)truncate { [log truncateFileAtOffset:0]; } -@end \ No newline at end of file +@end