From 35fefe5c3bc9fff005470d671bab022e52969af9 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 21 Oct 2016 12:05:38 -0400 Subject: [PATCH 001/127] Updated project file --- SQLClient/SQLClient.xcodeproj/project.pbxproj | 17 ++++++++++++++++- SQLClient/SQLClient/SQLClient-Info.plist | 2 +- .../SQLClientTests/SQLClientTests-Info.plist | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/SQLClient/SQLClient.xcodeproj/project.pbxproj b/SQLClient/SQLClient.xcodeproj/project.pbxproj index 2ba3a0f..d0bbd48 100644 --- a/SQLClient/SQLClient.xcodeproj/project.pbxproj +++ b/SQLClient/SQLClient.xcodeproj/project.pbxproj @@ -243,7 +243,7 @@ isa = PBXProject; attributes = { CLASSPREFIX = SQL; - LastUpgradeCheck = 0600; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = "Martin Rybak"; TargetAttributes = { 5B0FC86B180DCEB000DF4EFE = { @@ -378,13 +378,19 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 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", @@ -416,13 +422,18 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; + 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_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; @@ -452,6 +463,7 @@ "$(SRCROOT)/SQLClient/SQLClient", ); ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.martinrybak.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; VALID_ARCHS = "armv7 armv7s arm64"; WRAPPER_EXTENSION = app; @@ -475,6 +487,7 @@ "$(SRCROOT)/SQLClient/SQLClient", ); ONLY_ACTIVE_ARCH = NO; + PRODUCT_BUNDLE_IDENTIFIER = "com.martinrybak.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; VALID_ARCHS = "armv7 armv7s arm64"; WRAPPER_EXTENSION = app; @@ -497,6 +510,7 @@ "$(inherited)", ); INFOPLIST_FILE = "SQLClientTests/SQLClientTests-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "com.martinrybak.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; @@ -515,6 +529,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "SQLClient/SQLClient-Prefix.pch"; INFOPLIST_FILE = "SQLClientTests/SQLClientTests-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "com.martinrybak.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUNDLE_LOADER)"; WRAPPER_EXTENSION = xctest; diff --git a/SQLClient/SQLClient/SQLClient-Info.plist b/SQLClient/SQLClient/SQLClient-Info.plist index 493c572..173ceab 100644 --- a/SQLClient/SQLClient/SQLClient-Info.plist +++ b/SQLClient/SQLClient/SQLClient-Info.plist @@ -9,7 +9,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - com.martinrybak.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/SQLClient/SQLClientTests/SQLClientTests-Info.plist b/SQLClient/SQLClientTests/SQLClientTests-Info.plist index 8ccbca8..169b6f7 100644 --- a/SQLClient/SQLClientTests/SQLClientTests-Info.plist +++ b/SQLClient/SQLClientTests/SQLClientTests-Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - com.martinrybak.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType From c4f4786923dc79f7f7c34709a8c98e9bbd7ee56d Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 21 Oct 2016 12:10:01 -0400 Subject: [PATCH 002/127] Code cleanup --- SQLClient/SQLClient/SQLClient/SQLClient.m | 213 +++++++++++++--------- 1 file changed, 127 insertions(+), 86 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index bc40805..b75d80b 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -37,8 +37,8 @@ @interface SQLClient () @implementation SQLClient { - LOGINREC* login; - DBPROCESS* connection; + LOGINREC* _login; + DBPROCESS* _connection; char* _password; } @@ -50,8 +50,9 @@ - (id)init if (self = [super init]) { //Initialize the FreeTDS library - if (dbinit() == FAIL) + if (dbinit() == FAIL) { return nil; + } //Initialize SQLClient self.timeout = SQLClientDefaultTimeout; @@ -115,22 +116,31 @@ - (void)connect:(NSString*)host dbsetlogintime(self.timeout); //Initialize login struct - if ((login = dblogin()) == FAIL) - return [self connectionFailure:completion]; + _login = dblogin(); + if (_login == FAIL) { + [self connectionFailure:completion]; + return; + } //Populate login struct - DBSETLUSER(login, [self.username UTF8String]); - DBSETLPWD(login, _password); - DBSETLHOST(login, [self.host UTF8String]); - DBSETLCHARSET(login, [self.charset UTF8String]); + DBSETLUSER(_login, [self.username UTF8String]); + DBSETLPWD(_login, _password); + DBSETLHOST(_login, [self.host UTF8String]); + DBSETLCHARSET(_login, [self.charset UTF8String]); //Connect to database server - if ((connection = dbopen(login, [self.host UTF8String])) == NULL) - return [self connectionFailure:completion]; + _connection = dbopen(_login, [self.host UTF8String]); + if (_connection == NULL) { + [self connectionFailure:completion]; + return; + } //Switch to database - if (dbuse(connection, [self.database UTF8String]) == FAIL) - return [self connectionFailure:completion]; + RETCODE code = dbuse(_connection, [self.database UTF8String]); + if (code == FAIL) { + [self connectionFailure:completion]; + return; + } //Success! [self connectionSuccess:completion]; @@ -139,7 +149,7 @@ - (void)connect:(NSString*)host - (BOOL)connected { - return !dbdead(connection); + return !dbdead(_connection); } // TODO: how to get number of records changed during update or delete @@ -153,34 +163,45 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion dbsettime(self.timeout); //Prepare SQL statement - dbcmd(connection, [sql UTF8String]); + dbcmd(_connection, [sql UTF8String]); //Execute SQL statement - if (dbsqlexec(connection) == FAIL) - return [self executionFailure:completion]; + if (dbsqlexec(_connection) == FAIL) { + [self executionFailure:completion]; + return; + } //Create array to contain the tables - NSMutableArray* output = [[NSMutableArray alloc] init]; + NSMutableArray* output = [NSMutableArray array]; struct COL* columns; struct COL* pcol; int erc; - //Loop through each table - while ((erc = dbresults(connection)) != NO_MORE_RESULTS) + //Loop through each table metadata + //dbresults() returns SUCCEED, FAIL or, NO_MORE_RESULTS. + while ((erc = dbresults(_connection)) != NO_MORE_RESULTS) { + if (erc == FAIL) { + [self executionFailure:completion]; + return; + } + int ncols; int row_code; //Create array to contain the rows for this table - NSMutableArray* table = [[NSMutableArray alloc] init]; + NSMutableArray* table = [NSMutableArray array]; //Get number of columns - ncols = dbnumcols(connection); + ncols = dbnumcols(_connection); //Allocate C-style array of COL structs - if ((columns = calloc(ncols, sizeof(struct COL))) == NULL) - return [self executionFailure:completion]; + columns = calloc(ncols, sizeof(struct COL)); + if (columns == NULL) { + [self executionFailure:completion]; + return; + } //Bind the column info for (pcol = columns; pcol - columns < ncols; pcol++) @@ -189,37 +210,44 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion int c = pcol - columns + 1; //Get column metadata - pcol->name = dbcolname(connection, c); - pcol->type = dbcoltype(connection, c); - + pcol->name = dbcolname(_connection, c); + pcol->type = dbcoltype(_connection, c); + //For IMAGE data, we need to multiply by 2, because dbbind() will convert each byte to a hexadecimal pair. //http://www.freetds.org/userguide/samplecode.htm#SAMPLECODE.RESULTS - if(pcol->type == SYBIMAGE){ - pcol->size = dbcollen(connection, c) * 2; - }else{ - pcol->size = dbcollen(connection, c); + if (pcol->type == SYBIMAGE) { + pcol->size = dbcollen(_connection, c) * 2; + } else { + pcol->size = dbcollen(_connection, c); } - //If the column is [VAR]CHAR or TEXT, we want the column's defined size, otherwise we want //its maximum size when represented as a string, which FreeTDS's dbwillconvert() //returns (for fixed-length datatypes). We also do not need to convert IMAGE data type - if (pcol->type != SYBCHAR && pcol->type != SYBTEXT && pcol->type != SYBIMAGE) + if (pcol->type != SYBCHAR && pcol->type != SYBTEXT && pcol->type != SYBIMAGE) { pcol->size = dbwillconvert(pcol->type, SYBCHAR); + } //Allocate memory in the current pcol struct for a buffer - if ((pcol->buffer = calloc(1, pcol->size + 1)) == NULL) - return [self executionFailure:completion]; + pcol->buffer = calloc(1, pcol->size + 1); + if (pcol->buffer == NULL) { + [self executionFailure:completion]; + return; + } //Bind column name - erc = dbbind(connection, c, NTBSTRINGBIND, pcol->size + 1, (BYTE*)pcol->buffer); - if (erc == FAIL) - return [self executionFailure:completion]; + erc = dbbind(_connection, c, NTBSTRINGBIND, pcol->size + 1, (BYTE*)pcol->buffer); + if (erc == FAIL) { + [self executionFailure:completion]; + return; + } //Bind column status - erc = dbnullbind(connection, c, &pcol->status); - if (erc == FAIL) - return [self executionFailure:completion]; + erc = dbnullbind(_connection, c, &pcol->status); + if (erc == FAIL) { + [self executionFailure:completion]; + return; + } //printf("%s is type %d with value %s\n", pcol->name, pcol->type, pcol->buffer); } @@ -227,7 +255,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //printf("\n"); //Loop through each row - while ((row_code = dbnextrow(connection)) != NO_MORE_ROWS) + while ((row_code = dbnextrow(_connection)) != NO_MORE_ROWS) { //Check row type switch (row_code) @@ -245,24 +273,24 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion id value; if (pcol->status == -1) { //null value value = [NSNull null]; - + } + //Converting hexadecimal buffer into UIImage - }else if (pcol ->type == SYBIMAGE){ - NSString *hexString = [[NSString stringWithUTF8String:pcol->buffer] stringByReplacingOccurrencesOfString:@" " withString:@""]; - NSMutableData *hexData = [[NSMutableData alloc] init]; + else if (pcol->type == SYBIMAGE) { + NSString* hexString = [[NSString stringWithUTF8String:pcol->buffer] stringByReplacingOccurrencesOfString:@" " withString:@""]; + NSMutableData* hexData = [[NSMutableData alloc] init]; //Converting hex string to NSData unsigned char whole_byte; char byte_chars[3] = {'\0','\0','\0'}; - int i; - for (i=0; i < [hexString length]/2; i++) { - byte_chars[0] = [hexString characterAtIndex:i*2]; - byte_chars[1] = [hexString characterAtIndex:i*2+1]; + for (int i = 0; i < [hexString length] / 2; i++) { + byte_chars[0] = [hexString characterAtIndex:i * 2]; + byte_chars[1] = [hexString characterAtIndex:i * 2 + 1]; whole_byte = strtol(byte_chars, NULL, 16); [hexData appendBytes:&whole_byte length:1]; } value = [UIImage imageWithData:hexData]; - }else { + } else { value = [NSString stringWithUTF8String:pcol->buffer]; } //id value = [NSString stringWithUTF8String:pcol->buffer] ?: [NSNull null]; @@ -277,18 +305,22 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } //Buffer full case BUF_FULL: - return [self executionFailure:completion]; + [self executionFailure:completion]; + return; //Error case FAIL: - return [self executionFailure:completion]; + [self executionFailure:completion]; + return; default: [self message:SQLClientRowIgnoreMessage]; + break; } } //Clean up - for (pcol = columns; pcol - columns < ncols; pcol++) + for (pcol = columns; pcol - columns < ncols; pcol++) { free(pcol->buffer); + } free(columns); //Add immutable copy of table to output @@ -302,7 +334,29 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion - (void)disconnect { - dbclose(connection); + [self.workerQueue addOperationWithBlock:^{ + dbclose(_connection); + }]; +} + +#pragma mark - FreeTDS Callbacks + +//Handles message callback from FreeTDS library. +int msg_handler(DBPROCESS* dbproc, DBINT msgno, int msgstate, int severity, char* msgtext, char* srvname, char* procname, int line) +{ + //Can't call self from a C function, so need to access singleton + SQLClient* self = [SQLClient sharedInstance]; + [self message:[NSString stringWithUTF8String:msgtext]]; + return 0; +} + +//Handles error callback from FreeTDS library. +int err_handler(DBPROCESS* dbproc, int severity, int dberr, int oserr, char* dberrstr, char* oserrstr) +{ + //Can't call self from a C function, so need to access singleton + SQLClient* self = [SQLClient sharedInstance]; + [self error:[NSString stringWithUTF8String:dberrstr] code:dberr severity:severity]; + return INT_CANCEL; } #pragma mark - Private @@ -311,12 +365,13 @@ - (void)disconnect - (void)connectionFailure:(void (^)(BOOL success))completion { [self.callbackQueue addOperationWithBlock:^{ - if (completion) + if (completion) { completion(NO); + } }]; //Cleanup - dbloginfree(login); + dbloginfree(_login); free(_password); } @@ -324,12 +379,13 @@ - (void)connectionFailure:(void (^)(BOOL success))completion - (void)connectionSuccess:(void (^)(BOOL success))completion { [self.callbackQueue addOperationWithBlock:^{ - if (completion) - completion([self connected]); + if (completion) { + completion([self isConnected]); + } }]; //Cleanup - dbloginfree(login); + dbloginfree(_login); free(_password); } @@ -337,42 +393,26 @@ - (void)connectionSuccess:(void (^)(BOOL success))completion - (void)executionFailure:(void (^)(NSArray* results))completion { [self.callbackQueue addOperationWithBlock:^{ - if (completion) + if (completion) { completion(nil); + } }]; //Clean up - dbfreebuf(connection); + dbfreebuf(_connection); } //Invokes execution completion handler on callback queue with results array - (void)executionSuccess:(void (^)(NSArray* results))completion results:(NSArray*)results { [self.callbackQueue addOperationWithBlock:^{ - if (completion) + if (completion) { completion(results); + } }]; //Clean up - dbfreebuf(connection); -} - -//Handles message callback from FreeTDS library. -int msg_handler(DBPROCESS* dbproc, DBINT msgno, int msgstate, int severity, char* msgtext, char* srvname, char* procname, int line) -{ - //Can't call self from a C function, so need to access singleton - SQLClient* self = [SQLClient sharedInstance]; - [self message:[NSString stringWithUTF8String:msgtext]]; - return 0; -} - -//Handles error callback from FreeTDS library. -int err_handler(DBPROCESS* dbproc, int severity, int dberr, int oserr, char* dberrstr, char* oserrstr) -{ - //Can't call self from a C function, so need to access singleton - SQLClient* self = [SQLClient sharedInstance]; - [self error:[NSString stringWithUTF8String:dberrstr] code:dberr severity:severity]; - return INT_CANCEL; + dbfreebuf(_connection); } //Forwards a message to the delegate on the callback queue if it implements @@ -380,19 +420,20 @@ - (void)message:(NSString*)message { //Invoke delegate on calling queue [self.callbackQueue addOperationWithBlock:^{ - if ([self.delegate respondsToSelector:@selector(message:)]) + if ([self.delegate respondsToSelector:@selector(message:)]) { [self.delegate message:message]; + } }]; } //Forwards an error message to the delegate on the callback queue. - (void)error:(NSString*)error code:(int)code severity:(int)severity { - if (!self.delegate || ![self.delegate conformsToProtocol:@protocol(SQLClientDelegate)]) - [NSException raise:SQLClientDelegateError format:nil]; - //Invoke delegate on callback queue [self.callbackQueue addOperationWithBlock:^{ + if (![self.delegate conformsToProtocol:@protocol(SQLClientDelegate)]) { + [NSException raise:SQLClientDelegateError format:nil]; + } [self.delegate error:error code:code severity:severity]; }]; } From 02b11a8edaab867b466d1c8388330abbda57e012 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 21 Oct 2016 12:10:19 -0400 Subject: [PATCH 003/127] Renamed method to isConnected --- SQLClient/SQLClient/SQLClient/SQLClient.h | 2 +- SQLClient/SQLClient/SQLClient/SQLClient.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.h b/SQLClient/SQLClient/SQLClient/SQLClient.h index 094fdc6..77e40d0 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.h +++ b/SQLClient/SQLClient/SQLClient/SQLClient.h @@ -104,7 +104,7 @@ /** * Indicates whether the database is currently connected */ -- (BOOL)connected; +- (BOOL)isConnected; /** * Executes a SQL statement. Results of queries will be passed to the completion handler. Inserts, updates, and deletes do not return results. diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index b75d80b..c38daaf 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -147,7 +147,7 @@ - (void)connect:(NSString*)host }]; } -- (BOOL)connected +- (BOOL)isConnected { return !dbdead(_connection); } From ac457df3713995f96d88553961bb8b14d9242364 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 21 Oct 2016 12:10:32 -0400 Subject: [PATCH 004/127] Cleaned up sample code --- SQLClient/SQLClient/SQLViewController.m | 57 ++++++++++++++++--------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/SQLClient/SQLClient/SQLViewController.m b/SQLClient/SQLClient/SQLViewController.m index 3ed10b6..62b4363 100644 --- a/SQLClient/SQLClient/SQLViewController.m +++ b/SQLClient/SQLClient/SQLViewController.m @@ -18,37 +18,54 @@ @interface SQLViewController () @implementation SQLViewController +#pragma mark - UIViewController + - (void)viewDidLoad { [super viewDidLoad]; - - [self.spinner setHidesWhenStopped:YES]; - [self.spinner startAnimating]; - + [self.spinner setHidesWhenStopped:YES]; + [self connect]; +} + +#pragma mark - Private + +- (void)connect +{ SQLClient* client = [SQLClient sharedInstance]; client.delegate = self; + + [self.spinner startAnimating]; [client connect:@"server:port" username:@"user" password:@"pass" database:@"db" completion:^(BOOL success) { - if (success) - { - [client execute:@"SELECT * FROM Users" completion:^(NSArray* results) { - [self.spinner stopAnimating]; - [self process:results]; - [client disconnect]; - }]; + [self.spinner stopAnimating]; + if (success) { + [self execute]; } - else - [self.spinner stopAnimating]; }]; } -- (void)process:(NSArray*)data +- (void)execute { - NSMutableString* results = [[NSMutableString alloc] init]; - for (NSArray* table in data) - for (NSDictionary* row in table) - for (NSString* column in row) - [results appendFormat:@"\n%@=%@", column, row[column]]; - self.textView.text = results; + SQLClient* client = [SQLClient sharedInstance]; + + [self.spinner startAnimating]; + [client execute:@"SELECT * FROM Users" completion:^(NSArray* results) { + [self.spinner stopAnimating]; + [self process:results]; + [client disconnect]; + }]; +} + +- (void)process:(NSArray*)results +{ + NSMutableString* output = [[NSMutableString alloc] init]; + for (NSArray* table in results) { + for (NSDictionary* row in table) { + for (NSString* column in row) { + [output appendFormat:@"\n%@=%@", column, row[column]]; + } + } + } + self.textView.text = output; } #pragma mark - SQLClientDelegate From 7365232c70b0ef0d44fd7b106c50468339c9ed76 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 21 Oct 2016 12:40:58 -0400 Subject: [PATCH 005/127] Initial attempt at type handling --- SQLClient/SQLClient/SQLClient/SQLClient.m | 112 ++++++++++++++++++---- 1 file changed, 93 insertions(+), 19 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index c38daaf..49a4d86 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -269,31 +269,105 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Loop through each column and create an entry where dictionary[columnName] = columnValue for (pcol = columns; pcol - columns < ncols; pcol++) { - NSString* column = [NSString stringWithUTF8String:pcol->name]; id value; + if (pcol->status == -1) { //null value value = [NSNull null]; + } else { + switch (pcol->type) + { + case SYBBIT: + case SYBBITN: + { + bool bit; + dbbind(_connection, 1, BITBIND, 0, (BYTE*)&bit); + value = [NSNumber numberWithBool:bit]; + break; + } + case SYBINT1: + case SYBINT2: + case SYBINT4: + case SYBINT8: + case SYBINTN: //nullable + { + NSInteger integer; + dbbind(_connection, 1, INTBIND, 0, (BYTE*)&integer); + value = [NSNumber numberWithInteger:integer]; + break; + } + case SYBFLT8: + case SYBFLTN: //nullable + case SYBNUMERIC: + case SYBREAL: + { + CGFloat _float; + dbbind(_connection, 1, FLT8BIND, 0, (BYTE*)&_float); + value = [NSNumber numberWithFloat:_float]; + break; + } + case SYBMONEY4: + case SYBMONEY: + case SYBDECIMAL: + case SYBMONEYN: //nullable + { + //TODO + //[NSDecimalNumber decimalNumberWithDecimal:nil]; + break; + } + case SYBCHAR: + case SYBVARCHAR: + case SYBNVARCHAR: + case SYBTEXT: + case SYBNTEXT: + { + value = [NSString stringWithUTF8String:pcol->buffer]; + break; + } + case SYBDATETIME: + case SYBDATETIME4: + case SYBDATETIMN: + case SYBDATE: + case SYBTIME: + case SYBBIGDATETIME: + case SYBBIGTIME: + case SYBMSDATE: + case SYBMSTIME: + case SYBMSDATETIME2: + case SYBMSDATETIMEOFFSET: + { + //TODO + //NSDate + break; + } + case SYBIMAGE: + { + NSString* hexString = [[NSString stringWithUTF8String:pcol->buffer] stringByReplacingOccurrencesOfString:@" " withString:@""]; + NSMutableData* hexData = [[NSMutableData alloc] init]; + + //Converting hex string to NSData + unsigned char whole_byte; + char byte_chars[3] = {'\0','\0','\0'}; + for (int i = 0; i < [hexString length] / 2; i++) { + byte_chars[0] = [hexString characterAtIndex:i * 2]; + byte_chars[1] = [hexString characterAtIndex:i * 2 + 1]; + whole_byte = strtol(byte_chars, NULL, 16); + [hexData appendBytes:&whole_byte length:1]; + } + value = [UIImage imageWithData:hexData]; + break; + } + case SYBBINARY: + case SYBVOID: + case SYBVARBINARY: + { + value = [[NSData alloc] initWithBytes:pcol->buffer length:pcol->size]; + break; + } + } } - //Converting hexadecimal buffer into UIImage - else if (pcol->type == SYBIMAGE) { - NSString* hexString = [[NSString stringWithUTF8String:pcol->buffer] stringByReplacingOccurrencesOfString:@" " withString:@""]; - NSMutableData* hexData = [[NSMutableData alloc] init]; - - //Converting hex string to NSData - unsigned char whole_byte; - char byte_chars[3] = {'\0','\0','\0'}; - for (int i = 0; i < [hexString length] / 2; i++) { - byte_chars[0] = [hexString characterAtIndex:i * 2]; - byte_chars[1] = [hexString characterAtIndex:i * 2 + 1]; - whole_byte = strtol(byte_chars, NULL, 16); - [hexData appendBytes:&whole_byte length:1]; - } - value = [UIImage imageWithData:hexData]; - } else { - value = [NSString stringWithUTF8String:pcol->buffer]; - } //id value = [NSString stringWithUTF8String:pcol->buffer] ?: [NSNull null]; + NSString* column = [NSString stringWithUTF8String:pcol->name]; row[column] = value; //printf("%@=%@\n", column, value); } From 44d3ddb36cc96c08513db9ebbbdb182760247850 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 05:56:58 -0400 Subject: [PATCH 006/127] Dynamic type binding --- SQLClient/SQLClient/SQLClient/SQLClient.m | 193 ++++++++++++++-------- 1 file changed, 120 insertions(+), 73 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 49a4d86..d872867 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -21,7 +21,7 @@ struct COL { char* name; - char* buffer; + BYTE* buffer; int type; int size; int status; @@ -130,7 +130,7 @@ - (void)connect:(NSString*)host //Connect to database server _connection = dbopen(_login, [self.host UTF8String]); - if (_connection == NULL) { + if (!_connection) { [self connectionFailure:completion]; return; } @@ -173,78 +173,131 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Create array to contain the tables NSMutableArray* output = [NSMutableArray array]; - - struct COL* columns; - struct COL* pcol; - int erc; - //Loop through each table metadata //dbresults() returns SUCCEED, FAIL or, NO_MORE_RESULTS. - while ((erc = dbresults(_connection)) != NO_MORE_RESULTS) + RETCODE returnCode; + while ((returnCode = dbresults(_connection)) != NO_MORE_RESULTS) { - if (erc == FAIL) { + if (returnCode == FAIL) { [self executionFailure:completion]; return; } - int ncols; - int row_code; + int numColumns; + struct COL* columns; + struct COL* currentColumn; + STATUS rowCode; //Create array to contain the rows for this table NSMutableArray* table = [NSMutableArray array]; //Get number of columns - ncols = dbnumcols(_connection); + numColumns = dbnumcols(_connection); //Allocate C-style array of COL structs - columns = calloc(ncols, sizeof(struct COL)); - if (columns == NULL) { + columns = calloc(numColumns, sizeof(struct COL)); + if (!columns) { [self executionFailure:completion]; return; } //Bind the column info - for (pcol = columns; pcol - columns < ncols; pcol++) + for (currentColumn = columns; currentColumn - columns < numColumns; currentColumn++) { //Get column number - int c = pcol - columns + 1; + int c = currentColumn - columns + 1; //Get column metadata - pcol->name = dbcolname(_connection, c); - pcol->type = dbcoltype(_connection, c); - - //For IMAGE data, we need to multiply by 2, because dbbind() will convert each byte to a hexadecimal pair. - //http://www.freetds.org/userguide/samplecode.htm#SAMPLECODE.RESULTS - if (pcol->type == SYBIMAGE) { - pcol->size = dbcollen(_connection, c) * 2; - } else { - pcol->size = dbcollen(_connection, c); - } - - //If the column is [VAR]CHAR or TEXT, we want the column's defined size, otherwise we want - //its maximum size when represented as a string, which FreeTDS's dbwillconvert() - //returns (for fixed-length datatypes). We also do not need to convert IMAGE data type - if (pcol->type != SYBCHAR && pcol->type != SYBTEXT && pcol->type != SYBIMAGE) { - pcol->size = dbwillconvert(pcol->type, SYBCHAR); - } + currentColumn->name = dbcolname(_connection, c); + currentColumn->type = dbcoltype(_connection, c); + currentColumn->size = dbcollen(_connection, c); - //Allocate memory in the current pcol struct for a buffer - pcol->buffer = calloc(1, pcol->size + 1); - if (pcol->buffer == NULL) { + //Create buffer for column data + currentColumn->buffer = calloc(1, currentColumn->size); + if (!currentColumn->buffer) { [self executionFailure:completion]; return; } - //Bind column name - erc = dbbind(_connection, c, NTBSTRINGBIND, pcol->size + 1, (BYTE*)pcol->buffer); - if (erc == FAIL) { + //Set var type based on column type + int varType = 0; + switch (currentColumn->type) + { + case SYBBIT: + case SYBBITN: + { + varType = BITBIND; + break; + } + case SYBINT1: + case SYBINT2: + case SYBINT4: + case SYBINT8: + case SYBINTN: //nullable + { + varType = INTBIND; + break; + } + case SYBFLT8: + case SYBFLTN: //nullable + case SYBNUMERIC: + case SYBREAL: + { + varType = FLT8BIND; + break; + } + case SYBMONEY4: + case SYBMONEY: + case SYBDECIMAL: + case SYBMONEYN: //nullable + { + //TODO + break; + } + case SYBCHAR: + case SYBVARCHAR: + case SYBNVARCHAR: + case SYBTEXT: + case SYBNTEXT: + { + varType = NTBSTRINGBIND; + break; + } + case SYBDATETIME: + case SYBDATETIME4: + case SYBDATETIMN: + case SYBDATE: + case SYBTIME: + case SYBBIGDATETIME: + case SYBBIGTIME: + case SYBMSDATE: + case SYBMSTIME: + case SYBMSDATETIME2: + case SYBMSDATETIMEOFFSET: + { + //TODO + break; + } + case SYBBINARY: + case SYBVOID: + case SYBVARBINARY: + case SYBIMAGE: + { + varType = BINARYBIND; + break; + } + } + + //Bind column data + RETCODE returnCode = dbbind(_connection, c, varType, currentColumn->size, currentColumn->buffer); + if (returnCode == FAIL) { [self executionFailure:completion]; return; } - //Bind column status - erc = dbnullbind(_connection, c, &pcol->status); - if (erc == FAIL) { + //Bind null value into column status + returnCode = dbnullbind(_connection, c, ¤tColumn->status); + if (returnCode == FAIL) { [self executionFailure:completion]; return; } @@ -255,32 +308,33 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //printf("\n"); //Loop through each row - while ((row_code = dbnextrow(_connection)) != NO_MORE_ROWS) + while ((rowCode = dbnextrow(_connection)) != NO_MORE_ROWS) { //Check row type - switch (row_code) + switch (rowCode) { //Regular row case REG_ROW: { //Create a new dictionary to contain the column names and vaues - NSMutableDictionary* row = [[NSMutableDictionary alloc] initWithCapacity:ncols]; + NSMutableDictionary* row = [[NSMutableDictionary alloc] initWithCapacity:numColumns]; //Loop through each column and create an entry where dictionary[columnName] = columnValue - for (pcol = columns; pcol - columns < ncols; pcol++) + for (currentColumn = columns; currentColumn - columns < numColumns; currentColumn++) { + int c = currentColumn - columns + 1; id value; - if (pcol->status == -1) { //null value + if (currentColumn->status == -1) { //null value value = [NSNull null]; } else { - switch (pcol->type) + switch (currentColumn->type) { case SYBBIT: case SYBBITN: { bool bit; - dbbind(_connection, 1, BITBIND, 0, (BYTE*)&bit); + dbbind(_connection, c, BITBIND, currentColumn->size, (BYTE*)&bit); value = [NSNumber numberWithBool:bit]; break; } @@ -291,7 +345,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBINTN: //nullable { NSInteger integer; - dbbind(_connection, 1, INTBIND, 0, (BYTE*)&integer); + dbbind(_connection, c, INTBIND, currentColumn->size, (BYTE*)&integer); value = [NSNumber numberWithInteger:integer]; break; } @@ -301,7 +355,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBREAL: { CGFloat _float; - dbbind(_connection, 1, FLT8BIND, 0, (BYTE*)&_float); + dbbind(_connection, c, FLT8BIND, currentColumn->size, (BYTE*)&_float); value = [NSNumber numberWithFloat:_float]; break; } @@ -320,7 +374,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBTEXT: case SYBNTEXT: { - value = [NSString stringWithUTF8String:pcol->buffer]; + value = [NSString stringWithUTF8String:(char*)currentColumn->buffer]; break; } case SYBDATETIME: @@ -339,35 +393,28 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //NSDate break; } - case SYBIMAGE: - { - NSString* hexString = [[NSString stringWithUTF8String:pcol->buffer] stringByReplacingOccurrencesOfString:@" " withString:@""]; - NSMutableData* hexData = [[NSMutableData alloc] init]; - - //Converting hex string to NSData - unsigned char whole_byte; - char byte_chars[3] = {'\0','\0','\0'}; - for (int i = 0; i < [hexString length] / 2; i++) { - byte_chars[0] = [hexString characterAtIndex:i * 2]; - byte_chars[1] = [hexString characterAtIndex:i * 2 + 1]; - whole_byte = strtol(byte_chars, NULL, 16); - [hexData appendBytes:&whole_byte length:1]; - } - value = [UIImage imageWithData:hexData]; - break; - } case SYBBINARY: case SYBVOID: case SYBVARBINARY: { - value = [[NSData alloc] initWithBytes:pcol->buffer length:pcol->size]; + char* bytes; + dbbind(_connection, c, BINARYBIND, currentColumn->size, (BYTE*)&bytes); + value = [[NSData alloc] initWithBytes:bytes length:currentColumn->size]; + break; + } + case SYBIMAGE: + { + char* bytes; + dbbind(_connection, c, BINARYBIND, currentColumn->size, (BYTE*)&bytes); + NSData* data = [[NSData alloc] initWithBytes:bytes length:currentColumn->size]; + value = [UIImage imageWithData:data]; break; } } } //id value = [NSString stringWithUTF8String:pcol->buffer] ?: [NSNull null]; - NSString* column = [NSString stringWithUTF8String:pcol->name]; + NSString* column = [NSString stringWithUTF8String:currentColumn->name]; row[column] = value; //printf("%@=%@\n", column, value); } @@ -392,8 +439,8 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } //Clean up - for (pcol = columns; pcol - columns < ncols; pcol++) { - free(pcol->buffer); + for (currentColumn = columns; currentColumn - columns < numColumns; currentColumn++) { + free(currentColumn->buffer); } free(columns); From 2376917f78ea72888b6d9e4aba1a0ebfce8de931 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 06:52:23 -0400 Subject: [PATCH 007/127] Null values are handled when status = -1 --- SQLClient/SQLClient/SQLClient/SQLClient.m | 4 ---- 1 file changed, 4 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index d872867..dd956d0 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -331,7 +331,6 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion switch (currentColumn->type) { case SYBBIT: - case SYBBITN: { bool bit; dbbind(_connection, c, BITBIND, currentColumn->size, (BYTE*)&bit); @@ -342,7 +341,6 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBINT2: case SYBINT4: case SYBINT8: - case SYBINTN: //nullable { NSInteger integer; dbbind(_connection, c, INTBIND, currentColumn->size, (BYTE*)&integer); @@ -350,7 +348,6 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion break; } case SYBFLT8: - case SYBFLTN: //nullable case SYBNUMERIC: case SYBREAL: { @@ -362,7 +359,6 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBMONEY4: case SYBMONEY: case SYBDECIMAL: - case SYBMONEYN: //nullable { //TODO //[NSDecimalNumber decimalNumberWithDecimal:nil]; From 212b146b8343a0232e3ffedd6267897a1f840548 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 06:53:11 -0400 Subject: [PATCH 008/127] Got bit handling working --- SQLClient/SQLClient/SQLClient/SQLClient.m | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index dd956d0..5009cd1 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -332,9 +332,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion { case SYBBIT: { - bool bit; - dbbind(_connection, c, BITBIND, currentColumn->size, (BYTE*)&bit); - value = [NSNumber numberWithBool:bit]; + value = [NSNumber numberWithInt:currentColumn->buffer[0]]; break; } case SYBINT1: From fc4f6eee30457bfbdb953894a611c7be70c5ceec Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 07:05:29 -0400 Subject: [PATCH 009/127] Added support for integers --- SQLClient/SQLClient/SQLClient/SQLClient.m | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 5009cd1..3fd3f12 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -232,12 +232,16 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBINT1: case SYBINT2: case SYBINT4: - case SYBINT8: case SYBINTN: //nullable { varType = INTBIND; break; } + case SYBINT8: + { + varType = BIGINTBIND; + break; + } case SYBFLT8: case SYBFLTN: //nullable case SYBNUMERIC: @@ -338,11 +342,13 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBINT1: case SYBINT2: case SYBINT4: + { + value = [NSNumber numberWithInt:*(int32_t*)currentColumn->buffer]; + break; + } case SYBINT8: { - NSInteger integer; - dbbind(_connection, c, INTBIND, currentColumn->size, (BYTE*)&integer); - value = [NSNumber numberWithInteger:integer]; + value = [NSNumber numberWithLongLong:*(int64_t*)currentColumn->buffer]; break; } case SYBFLT8: From a4d3216d8639c593d5a9748235ed604027a57b3e Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 07:15:57 -0400 Subject: [PATCH 010/127] Added more specific int binders --- SQLClient/SQLClient/SQLClient/SQLClient.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 3fd3f12..7e39634 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -230,7 +230,15 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion break; } case SYBINT1: + { + varType = TINYBIND; + break; + } case SYBINT2: + { + varType = SMALLBIND; + break; + } case SYBINT4: case SYBINTN: //nullable { From a2542307e543ee799ad372d71b15b45b38ed53a1 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 07:26:58 -0400 Subject: [PATCH 011/127] Better way of extracting ints and longs --- SQLClient/SQLClient/SQLClient/SQLClient.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 7e39634..2b480c8 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -351,12 +351,16 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBINT2: case SYBINT4: { - value = [NSNumber numberWithInt:*(int32_t*)currentColumn->buffer]; + int32_t _value; + memcpy(&_value, currentColumn->buffer, sizeof _value); + value = [NSNumber numberWithInt:_value]; break; } case SYBINT8: { - value = [NSNumber numberWithLongLong:*(int64_t*)currentColumn->buffer]; + long long _value; + memcpy(&_value, currentColumn->buffer, sizeof _value); + value = [NSNumber numberWithLongLong:_value]; break; } case SYBFLT8: From 2601abea6fbbfdb4eb3d65bf60f431edcd84b8e8 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 07:27:16 -0400 Subject: [PATCH 012/127] Added support for floats --- SQLClient/SQLClient/SQLClient/SQLClient.m | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 2b480c8..752327a 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -252,12 +252,20 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } case SYBFLT8: case SYBFLTN: //nullable - case SYBNUMERIC: - case SYBREAL: { varType = FLT8BIND; break; } + case SYBREAL: + { + varType = REALBIND; + break; + } + case SYBNUMERIC: + { + varType = NUMERICBIND; + break; + } case SYBMONEY4: case SYBMONEY: case SYBDECIMAL: @@ -364,17 +372,17 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion break; } case SYBFLT8: - case SYBNUMERIC: case SYBREAL: { - CGFloat _float; - dbbind(_connection, c, FLT8BIND, currentColumn->size, (BYTE*)&_float); - value = [NSNumber numberWithFloat:_float]; + double _value; + memcpy(&_value, currentColumn->buffer, sizeof _value); + value = [NSNumber numberWithDouble:_value]; break; } case SYBMONEY4: case SYBMONEY: case SYBDECIMAL: + case SYBNUMERIC: { //TODO //[NSDecimalNumber decimalNumberWithDecimal:nil]; From 4c22e28a748dffb257dfc793a805c18a30627392 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 07:32:11 -0400 Subject: [PATCH 013/127] Better way of converting to bool --- SQLClient/SQLClient/SQLClient/SQLClient.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 752327a..1a5341d 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -352,7 +352,9 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion { case SYBBIT: { - value = [NSNumber numberWithInt:currentColumn->buffer[0]]; + BOOL _value; + memcpy(&_value, currentColumn->buffer, sizeof _value); + value = [NSNumber numberWithBool:_value]; break; } case SYBINT1: From f03085de96a7402415af6951e5b96a319aecc949 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 07:32:30 -0400 Subject: [PATCH 014/127] Added support for real data type --- SQLClient/SQLClient/SQLClient/SQLClient.m | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 1a5341d..c5249d9 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -374,13 +374,19 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion break; } case SYBFLT8: - case SYBREAL: { double _value; memcpy(&_value, currentColumn->buffer, sizeof _value); value = [NSNumber numberWithDouble:_value]; break; } + case SYBREAL: + { + float _value; + memcpy(&_value, currentColumn->buffer, sizeof _value); + value = [NSNumber numberWithFloat:_value]; + break; + } case SYBMONEY4: case SYBMONEY: case SYBDECIMAL: From 1fd4e343519f54514bd2acb4b49387b36716561a Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 07:32:44 -0400 Subject: [PATCH 015/127] Updated comment --- SQLClient/SQLClient/SQLClient/SQLClient.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index c5249d9..1c98f0e 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -173,6 +173,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Create array to contain the tables NSMutableArray* output = [NSMutableArray array]; + //Loop through each table metadata //dbresults() returns SUCCEED, FAIL or, NO_MORE_RESULTS. RETCODE returnCode; @@ -219,7 +220,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion return; } - //Set var type based on column type + //Set bind type based on column type int varType = 0; switch (currentColumn->type) { From 5976fdf8cef054b67ff572997fc4ea2d3afefe9b Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 11:03:59 -0400 Subject: [PATCH 016/127] Added money conversion --- SQLClient/SQLClient/SQLClient/SQLClient.m | 28 +++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 1c98f0e..197926a 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -268,7 +268,16 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion break; } case SYBMONEY4: + { + varType = SMALLMONEYBIND; + break; + } case SYBMONEY: + case SYBMONEYN: + { + varType = MONEYBIND; + break; + } case SYBDECIMAL: case SYBMONEYN: //nullable { @@ -388,8 +397,23 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion value = [NSNumber numberWithFloat:_value]; break; } - case SYBMONEY4: - case SYBMONEY: + case SYBMONEY4: //Monetary data from -214,748.3648 to 214,748.3647 + { + DBMONEY4 _money; + memcpy(&_money, currentColumn->buffer, sizeof _money); + NSNumber* _value = @(_money.mny4); + NSDecimalNumber* decimalNumber = [NSDecimalNumber decimalNumberWithString:[_value description]]; + value = [decimalNumber decimalNumberByDividingBy:[NSDecimalNumber decimalNumberWithString:@"10000"]]; + break; + } + case SYBMONEY: //Monetary data from -922,337,203,685,477.5808 to 922,337,203,685,477.5807 + { + BYTE* string = calloc(20, sizeof(char)); //Max string length is 20 + dbconvert(_connection, SYBMONEY, currentColumn->buffer, sizeof(SYBMONEY), SYBCHAR, string, -1); + value = [NSDecimalNumber decimalNumberWithString:[NSString stringWithUTF8String:(char*)string]]; + free(string); + break; + } case SYBDECIMAL: case SYBNUMERIC: { From a995cbae09a0759ad9cfcffc7b17dff0bfc9e50b Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 11:04:11 -0400 Subject: [PATCH 017/127] Updated comments --- SQLClient/SQLClient/SQLClient/SQLClient.m | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 197926a..6b14a2b 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -241,7 +241,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion break; } case SYBINT4: - case SYBINTN: //nullable + case SYBINTN: { varType = INTBIND; break; @@ -252,7 +252,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion break; } case SYBFLT8: - case SYBFLTN: //nullable + case SYBFLTN: { varType = FLT8BIND; break; @@ -279,7 +279,6 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion break; } case SYBDECIMAL: - case SYBMONEYN: //nullable { //TODO break; @@ -360,37 +359,37 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } else { switch (currentColumn->type) { - case SYBBIT: + case SYBBIT: //0 or 1 { BOOL _value; memcpy(&_value, currentColumn->buffer, sizeof _value); value = [NSNumber numberWithBool:_value]; break; } - case SYBINT1: - case SYBINT2: - case SYBINT4: + case SYBINT1: //Whole numbers from 0 to 255 + case SYBINT2: //Whole numbers between -32,768 and 32,767 + case SYBINT4: //Whole numbers between -2,147,483,648 and 2,147,483,647 { int32_t _value; memcpy(&_value, currentColumn->buffer, sizeof _value); value = [NSNumber numberWithInt:_value]; break; } - case SYBINT8: + case SYBINT8: //Whole numbers between -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807 { long long _value; memcpy(&_value, currentColumn->buffer, sizeof _value); value = [NSNumber numberWithLongLong:_value]; break; } - case SYBFLT8: + case SYBFLT8: //Floating precision number data from -1.79E + 308 to 1.79E + 308 { double _value; memcpy(&_value, currentColumn->buffer, sizeof _value); value = [NSNumber numberWithDouble:_value]; break; } - case SYBREAL: + case SYBREAL: //Floating precision number data from -3.40E + 38 to 3.40E + 38 { float _value; memcpy(&_value, currentColumn->buffer, sizeof _value); From 7bd043b7fa155b9ac4d88e57529f681fd6e682ee Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 11:41:03 -0400 Subject: [PATCH 018/127] Added decimal and numeric handling --- SQLClient/SQLClient/SQLClient/SQLClient.m | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 6b14a2b..50e5543 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -262,11 +262,6 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion varType = REALBIND; break; } - case SYBNUMERIC: - { - varType = NUMERICBIND; - break; - } case SYBMONEY4: { varType = SMALLMONEYBIND; @@ -279,10 +274,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion break; } case SYBDECIMAL: - { - //TODO - break; - } + case SYBNUMERIC: case SYBCHAR: case SYBVARCHAR: case SYBNVARCHAR: @@ -413,11 +405,11 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion free(string); break; } - case SYBDECIMAL: + case SYBDECIMAL: //Numbers from -10^38 +1 to 10^38 –1. case SYBNUMERIC: { - //TODO - //[NSDecimalNumber decimalNumberWithDecimal:nil]; + NSString* _value = [[NSString alloc] initWithUTF8String:(char*)currentColumn->buffer]; + value = [NSDecimalNumber decimalNumberWithString:_value]; break; } case SYBCHAR: From efa66e5c0b115f891ed2568b592ed70e7b00ca3a Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 14:55:49 -0400 Subject: [PATCH 019/127] Added placeholders for NSData and UIImage --- SQLClient/SQLClient/SQLClient/SQLClient.m | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 50e5543..d20d1b8 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -441,17 +441,14 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBVOID: case SYBVARBINARY: { - char* bytes; - dbbind(_connection, c, BINARYBIND, currentColumn->size, (BYTE*)&bytes); - value = [[NSData alloc] initWithBytes:bytes length:currentColumn->size]; + //TODO + //NSData break; } case SYBIMAGE: { - char* bytes; - dbbind(_connection, c, BINARYBIND, currentColumn->size, (BYTE*)&bytes); - NSData* data = [[NSData alloc] initWithBytes:bytes length:currentColumn->size]; - value = [UIImage imageWithData:data]; + //TODO + //UIImage break; } } From f842e69ab90a46e26908f25ca1fd462d04d64cf5 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 14:56:24 -0400 Subject: [PATCH 020/127] Updated sizeof type for consistency --- SQLClient/SQLClient/SQLClient/SQLClient.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index d20d1b8..7919291 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -399,7 +399,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } case SYBMONEY: //Monetary data from -922,337,203,685,477.5808 to 922,337,203,685,477.5807 { - BYTE* string = calloc(20, sizeof(char)); //Max string length is 20 + BYTE* string = calloc(20, sizeof(BYTE)); //Max string length is 20 dbconvert(_connection, SYBMONEY, currentColumn->buffer, sizeof(SYBMONEY), SYBCHAR, string, -1); value = [NSDecimalNumber decimalNumberWithString:[NSString stringWithUTF8String:(char*)string]]; free(string); From 15b9b238011e970b35cc9d38003fa079c5bef802 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 14:56:35 -0400 Subject: [PATCH 021/127] Removed unneeded line --- SQLClient/SQLClient/SQLClient/SQLClient.m | 1 - 1 file changed, 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 7919291..71bfaa5 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -343,7 +343,6 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Loop through each column and create an entry where dictionary[columnName] = columnValue for (currentColumn = columns; currentColumn - columns < numColumns; currentColumn++) { - int c = currentColumn - columns + 1; id value; if (currentColumn->status == -1) { //null value From d015b44d10ebd8da79e7c62e6b34e92b85d7f14c Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 16:43:39 -0400 Subject: [PATCH 022/127] Added support for NSData and UIImage --- SQLClient/SQLClient/SQLClient/SQLClient.m | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 71bfaa5..af1a398 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -436,18 +436,17 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //NSDate break; } - case SYBBINARY: case SYBVOID: + case SYBBINARY: case SYBVARBINARY: { - //TODO - //NSData + value = [NSData dataWithBytes:currentColumn->buffer length:currentColumn->size]; break; } case SYBIMAGE: { - //TODO - //UIImage + NSData* data = [NSData dataWithBytes:currentColumn->buffer length:currentColumn->size]; + value = [UIImage imageWithData:data]; break; } } From af0841f4260c26572359491f25d6229307afb416 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sun, 23 Oct 2016 21:15:47 -0400 Subject: [PATCH 023/127] Added support for NSDate --- SQLClient/SQLClient/SQLClient/SQLClient.m | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index af1a398..e511e2d 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -296,7 +296,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBMSDATETIME2: case SYBMSDATETIMEOFFSET: { - //TODO + varType = DATETIMEBIND; break; } case SYBBINARY: @@ -432,8 +432,12 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBMSDATETIME2: case SYBMSDATETIMEOFFSET: { - //TODO - //NSDate + DBDATETIME _value; + memcpy(&_value, currentColumn->buffer, sizeof _value); + NSTimeInterval daysSinceReferenceDate = (NSTimeInterval)_value.dtdays; //Days are counted from 1/1/1900 + NSTimeInterval secondsSinceReferenceDate = daysSinceReferenceDate * 24 * 60 * 60; + NSTimeInterval secondsSinceMidnight = _value.dttime / 3000; //Time is in increments of 3.33 milliseconds + value = [NSDate dateWithTimeInterval:secondsSinceReferenceDate + secondsSinceMidnight sinceDate:[self referenceDate]]; break; } case SYBVOID: @@ -598,4 +602,15 @@ - (void)error:(NSString*)error code:(int)code severity:(int)severity }]; } +//January 1, 1900 +- (NSDate*)referenceDate +{ + NSCalendar* calendar = [NSCalendar currentCalendar]; + NSDateComponents* dateComponents = [[NSDateComponents alloc] init]; + dateComponents.year = 1900; + dateComponents.month = 1; + dateComponents.day = 1; + return [calendar dateFromComponents:dateComponents]; +} + @end From e50aadd7100257dce5a28b90b34ae690924849a8 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 05:53:47 -0400 Subject: [PATCH 024/127] Renamed current column variable --- SQLClient/SQLClient/SQLClient/SQLClient.m | 69 +++++++++++------------ 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index e511e2d..ca06876 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -186,7 +186,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion int numColumns; struct COL* columns; - struct COL* currentColumn; + struct COL* column; STATUS rowCode; //Create array to contain the rows for this table @@ -203,26 +203,26 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } //Bind the column info - for (currentColumn = columns; currentColumn - columns < numColumns; currentColumn++) + for (column = columns; column - columns < numColumns; column++) { //Get column number - int c = currentColumn - columns + 1; + int c = column - columns + 1; //Get column metadata - currentColumn->name = dbcolname(_connection, c); - currentColumn->type = dbcoltype(_connection, c); - currentColumn->size = dbcollen(_connection, c); + column->name = dbcolname(_connection, c); + column->type = dbcoltype(_connection, c); + column->size = dbcollen(_connection, c); //Create buffer for column data - currentColumn->buffer = calloc(1, currentColumn->size); - if (!currentColumn->buffer) { + column->buffer = calloc(1, column->size); + if (!column->buffer) { [self executionFailure:completion]; return; } //Set bind type based on column type int varType = 0; - switch (currentColumn->type) + switch (column->type) { case SYBBIT: case SYBBITN: @@ -287,13 +287,13 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBDATETIME: case SYBDATETIME4: case SYBDATETIMN: - case SYBDATE: +// case SYBDATE: case SYBTIME: case SYBBIGDATETIME: case SYBBIGTIME: - case SYBMSDATE: +// case SYBMSDATE: case SYBMSTIME: - case SYBMSDATETIME2: +// case SYBMSDATETIME2: case SYBMSDATETIMEOFFSET: { varType = DATETIMEBIND; @@ -310,14 +310,14 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } //Bind column data - RETCODE returnCode = dbbind(_connection, c, varType, currentColumn->size, currentColumn->buffer); + RETCODE returnCode = dbbind(_connection, c, varType, column->size, column->buffer); if (returnCode == FAIL) { [self executionFailure:completion]; return; } //Bind null value into column status - returnCode = dbnullbind(_connection, c, ¤tColumn->status); + returnCode = dbnullbind(_connection, c, &column->status); if (returnCode == FAIL) { [self executionFailure:completion]; return; @@ -341,19 +341,19 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion NSMutableDictionary* row = [[NSMutableDictionary alloc] initWithCapacity:numColumns]; //Loop through each column and create an entry where dictionary[columnName] = columnValue - for (currentColumn = columns; currentColumn - columns < numColumns; currentColumn++) + for (column = columns; column - columns < numColumns; column++) { id value; - if (currentColumn->status == -1) { //null value + if (column->status == -1) { //null value value = [NSNull null]; } else { - switch (currentColumn->type) + switch (column->type) { case SYBBIT: //0 or 1 { BOOL _value; - memcpy(&_value, currentColumn->buffer, sizeof _value); + memcpy(&_value, column->buffer, sizeof _value); value = [NSNumber numberWithBool:_value]; break; } @@ -362,35 +362,35 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBINT4: //Whole numbers between -2,147,483,648 and 2,147,483,647 { int32_t _value; - memcpy(&_value, currentColumn->buffer, sizeof _value); + memcpy(&_value, column->buffer, sizeof _value); value = [NSNumber numberWithInt:_value]; break; } case SYBINT8: //Whole numbers between -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807 { long long _value; - memcpy(&_value, currentColumn->buffer, sizeof _value); + memcpy(&_value, column->buffer, sizeof _value); value = [NSNumber numberWithLongLong:_value]; break; } case SYBFLT8: //Floating precision number data from -1.79E + 308 to 1.79E + 308 { double _value; - memcpy(&_value, currentColumn->buffer, sizeof _value); + memcpy(&_value, column->buffer, sizeof _value); value = [NSNumber numberWithDouble:_value]; break; } case SYBREAL: //Floating precision number data from -3.40E + 38 to 3.40E + 38 { float _value; - memcpy(&_value, currentColumn->buffer, sizeof _value); + memcpy(&_value, column->buffer, sizeof _value); value = [NSNumber numberWithFloat:_value]; break; } case SYBMONEY4: //Monetary data from -214,748.3648 to 214,748.3647 { DBMONEY4 _money; - memcpy(&_money, currentColumn->buffer, sizeof _money); + memcpy(&_money, column->buffer, sizeof _money); NSNumber* _value = @(_money.mny4); NSDecimalNumber* decimalNumber = [NSDecimalNumber decimalNumberWithString:[_value description]]; value = [decimalNumber decimalNumberByDividingBy:[NSDecimalNumber decimalNumberWithString:@"10000"]]; @@ -399,7 +399,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBMONEY: //Monetary data from -922,337,203,685,477.5808 to 922,337,203,685,477.5807 { BYTE* string = calloc(20, sizeof(BYTE)); //Max string length is 20 - dbconvert(_connection, SYBMONEY, currentColumn->buffer, sizeof(SYBMONEY), SYBCHAR, string, -1); + dbconvert(_connection, SYBMONEY, column->buffer, sizeof(SYBMONEY), SYBCHAR, string, -1); value = [NSDecimalNumber decimalNumberWithString:[NSString stringWithUTF8String:(char*)string]]; free(string); break; @@ -407,7 +407,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBDECIMAL: //Numbers from -10^38 +1 to 10^38 –1. case SYBNUMERIC: { - NSString* _value = [[NSString alloc] initWithUTF8String:(char*)currentColumn->buffer]; + NSString* _value = [[NSString alloc] initWithUTF8String:(char*)column->buffer]; value = [NSDecimalNumber decimalNumberWithString:_value]; break; } @@ -417,7 +417,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBTEXT: case SYBNTEXT: { - value = [NSString stringWithUTF8String:(char*)currentColumn->buffer]; + value = [NSString stringWithUTF8String:(char*)column->buffer]; break; } case SYBDATETIME: @@ -433,7 +433,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBMSDATETIMEOFFSET: { DBDATETIME _value; - memcpy(&_value, currentColumn->buffer, sizeof _value); + memcpy(&_value, column->buffer, sizeof _value); NSTimeInterval daysSinceReferenceDate = (NSTimeInterval)_value.dtdays; //Days are counted from 1/1/1900 NSTimeInterval secondsSinceReferenceDate = daysSinceReferenceDate * 24 * 60 * 60; NSTimeInterval secondsSinceMidnight = _value.dttime / 3000; //Time is in increments of 3.33 milliseconds @@ -444,27 +444,24 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBBINARY: case SYBVARBINARY: { - value = [NSData dataWithBytes:currentColumn->buffer length:currentColumn->size]; + value = [NSData dataWithBytes:column->buffer length:column->size]; break; } case SYBIMAGE: { - NSData* data = [NSData dataWithBytes:currentColumn->buffer length:currentColumn->size]; + NSData* data = [NSData dataWithBytes:column->buffer length:column->size]; value = [UIImage imageWithData:data]; break; } } } - //id value = [NSString stringWithUTF8String:pcol->buffer] ?: [NSNull null]; - NSString* column = [NSString stringWithUTF8String:currentColumn->name]; - row[column] = value; - //printf("%@=%@\n", column, value); + NSString* columnName = [NSString stringWithUTF8String:column->name]; + row[columnName] = value; } //Add an immutable copy to the table [table addObject:[row copy]]; - //printf("\n"); break; } //Buffer full @@ -482,8 +479,8 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } //Clean up - for (currentColumn = columns; currentColumn - columns < numColumns; currentColumn++) { - free(currentColumn->buffer); + for (column = columns; column - columns < numColumns; column++) { + free(column->buffer); } free(columns); From a2b2f25e27371e9b230f18e89af322f1a6b8b2dd Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 07:27:10 -0400 Subject: [PATCH 025/127] Added support for uniqueidentifier --- SQLClient/SQLClient/SQLClient/SQLClient.m | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index ca06876..49b4613 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -11,6 +11,8 @@ #import "sybdb.h" #import "syberror.h" +#define SYBUNIQUEIDENTIFIER 36 + int const SQLClientDefaultTimeout = 5; int const SQLClientDefaultQueryTimeout = 5; NSString* const SQLClientDefaultCharset = @"UTF-8"; @@ -307,6 +309,11 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion varType = BINARYBIND; break; } + case SYBUNIQUEIDENTIFIER: + { + varType = BINARYBIND; + break; + } } //Bind column data @@ -453,6 +460,11 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion value = [UIImage imageWithData:data]; break; } + case SYBUNIQUEIDENTIFIER: + { + value = [[NSUUID alloc] initWithUUIDBytes:column->buffer]; + break; + } } } From 78da38d2ab78be4b7cf02a1876e91069ec47dd71 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 07:28:56 -0400 Subject: [PATCH 026/127] Updated readme with data types --- README.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 409790a..27bd1c8 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,13 @@ client.delegate = self; if (success) { [client execute:@"SELECT * FROM Users" completion:^(NSArray* results) { - for (NSArray* table in results) - for (NSDictionary* row in table) - for (NSString* column in row) + for (NSArray* table in results) { + for (NSDictionary* row in table) { + for (NSString* column in row) { NSLog(@"%@=%@", column, row[column]); + } + } + } [client disconnect]; }]; } @@ -30,6 +33,60 @@ client.delegate = self; } +##Type Conversion + +* bigint -> NSNumber +* binary(n) -> NSData +* bit -> NSNumber +* char(n) -> NSString +* cursor ? +* date -> **NSString** +* datetime -> NSDate +* datetime2 -> **NSString** +* datetimeoffset -> **NSString** +* decimal(p,s) -> NSNumber +* float(n) -> NSNumber +* image -> UIImage +* int -> NSNumber +* money -> NSDecimalNumber **(last 2 digits are truncated)** +* nchar -> NSString +* ntext -> NSString +* numeric(p,s) -> NSNumber +* nvarchar -> NSString +* nvarchar(max) -> NSString +* real -> NSNumber +* smalldatetime -> NSDate +* smallint -> NSNumber +* smallmoney -> NSDecimalNumber +* sql_variant -> ? +* table -> ? +* text -> NSString +* time -> **NSString** +* timestamp -> ? +* tinyint -> NSNumber +* uniqueidentifier -> NSUUID +* varbinary -> NSData +* varbinary(max) -> NSData +* varchar(max) -> NSString +* varchar(n) -> NSString +* xml -> + +##Testing + +The type conversions have been tested with SQL Server 2008 R2. + +## Known Issues + +* **money**: FreeTDS will truncate the rightmost 2 digits. + +* The following data types are recognized by FreeTDS as type **47 (SYBCHAR)**, so SQLClient can't convert them into proper objects: + + * datetime2 + * date + * datetimeoffset + * time + + ##Demo Project Open the Xcode project inside the **SQLClient** folder. From 1e63a00e0377d236c36610095db9cd9d98d75ce0 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 07:29:50 -0400 Subject: [PATCH 027/127] Use macro variable --- SQLClient/SQLClient/SQLClient/SQLClient.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 49b4613..3cd2f49 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -223,7 +223,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } //Set bind type based on column type - int varType = 0; + int varType = CHARBIND; //Default switch (column->type) { case SYBBIT: From 0e98abee05d7e0ac77a4c3bb88895fb96998fb34 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 08:57:06 -0400 Subject: [PATCH 028/127] Updated memory cleanup --- SQLClient/SQLClient/SQLClient/SQLClient.m | 75 ++++++++++++++--------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 3cd2f49..9ef127c 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -42,6 +42,7 @@ @implementation SQLClient LOGINREC* _login; DBPROCESS* _connection; char* _password; + struct COL* _columns; } #pragma mark - NSObject @@ -120,6 +121,7 @@ - (void)connect:(NSString*)host //Initialize login struct _login = dblogin(); if (_login == FAIL) { + [self cleanupAfterConnection]; [self connectionFailure:completion]; return; } @@ -133,6 +135,7 @@ - (void)connect:(NSString*)host //Connect to database server _connection = dbopen(_login, [self.host UTF8String]); if (!_connection) { + [self cleanupAfterConnection]; [self connectionFailure:completion]; return; } @@ -140,12 +143,14 @@ - (void)connect:(NSString*)host //Switch to database RETCODE code = dbuse(_connection, [self.database UTF8String]); if (code == FAIL) { + [self cleanupAfterConnection]; [self connectionFailure:completion]; return; } //Success! [self connectionSuccess:completion]; + [self cleanupAfterConnection]; }]; } @@ -169,6 +174,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Execute SQL statement if (dbsqlexec(_connection) == FAIL) { + [self cleanupAfterExecution:0]; [self executionFailure:completion]; return; } @@ -182,12 +188,12 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion while ((returnCode = dbresults(_connection)) != NO_MORE_RESULTS) { if (returnCode == FAIL) { + [self cleanupAfterExecution:0]; [self executionFailure:completion]; return; } int numColumns; - struct COL* columns; struct COL* column; STATUS rowCode; @@ -198,17 +204,18 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion numColumns = dbnumcols(_connection); //Allocate C-style array of COL structs - columns = calloc(numColumns, sizeof(struct COL)); - if (!columns) { + _columns = calloc(numColumns, sizeof(struct COL)); + if (!_columns) { + [self cleanupAfterExecution:0]; [self executionFailure:completion]; return; } //Bind the column info - for (column = columns; column - columns < numColumns; column++) + for (column = _columns; column - _columns < numColumns; column++) { //Get column number - int c = column - columns + 1; + int c = column - _columns + 1; //Get column metadata column->name = dbcolname(_connection, c); @@ -218,6 +225,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Create buffer for column data column->buffer = calloc(1, column->size); if (!column->buffer) { + [self cleanupAfterExecution:numColumns]; [self executionFailure:completion]; return; } @@ -319,6 +327,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Bind column data RETCODE returnCode = dbbind(_connection, c, varType, column->size, column->buffer); if (returnCode == FAIL) { + [self cleanupAfterExecution:numColumns]; [self executionFailure:completion]; return; } @@ -326,6 +335,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Bind null value into column status returnCode = dbnullbind(_connection, c, &column->status); if (returnCode == FAIL) { + [self cleanupAfterExecution:numColumns]; [self executionFailure:completion]; return; } @@ -348,11 +358,12 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion NSMutableDictionary* row = [[NSMutableDictionary alloc] initWithCapacity:numColumns]; //Loop through each column and create an entry where dictionary[columnName] = columnValue - for (column = columns; column - columns < numColumns; column++) + for (column = _columns; column - _columns < numColumns; column++) { id value; - if (column->status == -1) { //null value + //Check for null + if (column->status == -1) { value = [NSNull null]; } else { switch (column->type) @@ -478,10 +489,12 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } //Buffer full case BUF_FULL: + [self cleanupAfterExecution:numColumns]; [self executionFailure:completion]; return; //Error case FAIL: + [self cleanupAfterExecution:numColumns]; [self executionFailure:completion]; return; default: @@ -489,15 +502,10 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion break; } } - - //Clean up - for (column = columns; column - columns < numColumns; column++) { - free(column->buffer); - } - free(columns); - + //Add immutable copy of table to output [output addObject:[table copy]]; + [self cleanupAfterExecution:numColumns]; } //Success! Send an immutable copy of the results array @@ -532,6 +540,31 @@ int err_handler(DBPROCESS* dbproc, int severity, int dberr, int oserr, char* dbe return INT_CANCEL; } + +#pragma mark - Cleanup + +- (void)cleanupAfterConnection +{ + dbloginfree(_login); + if (_password) { + free(_password); + } +} + +- (void)cleanupAfterExecution:(int)numColumns +{ + struct COL* column; + for (column = _columns; column - _columns < numColumns; column++) { + if (column->buffer) { + free(column->buffer); + } + } + if (_columns) { + free(_columns); + } + dbfreebuf(_connection); +} + #pragma mark - Private //Invokes connection completion handler on callback queue with success = NO @@ -542,10 +575,6 @@ - (void)connectionFailure:(void (^)(BOOL success))completion completion(NO); } }]; - - //Cleanup - dbloginfree(_login); - free(_password); } //Invokes connection completion handler on callback queue with success = [self connected] @@ -556,10 +585,6 @@ - (void)connectionSuccess:(void (^)(BOOL success))completion completion([self isConnected]); } }]; - - //Cleanup - dbloginfree(_login); - free(_password); } //Invokes execution completion handler on callback queue with results = nil @@ -570,9 +595,6 @@ - (void)executionFailure:(void (^)(NSArray* results))completion completion(nil); } }]; - - //Clean up - dbfreebuf(_connection); } //Invokes execution completion handler on callback queue with results array @@ -583,9 +605,6 @@ - (void)executionSuccess:(void (^)(NSArray* results))completion results:(NSArray completion(results); } }]; - - //Clean up - dbfreebuf(_connection); } //Forwards a message to the delegate on the callback queue if it implements From f8a8fca79d6c6c02727654cc1d50062e35995485 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 08:57:18 -0400 Subject: [PATCH 029/127] Renamed variables --- SQLClient/SQLClient/SQLClient/SQLClient.m | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 9ef127c..c09a240 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -407,19 +407,19 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } case SYBMONEY4: //Monetary data from -214,748.3648 to 214,748.3647 { - DBMONEY4 _money; - memcpy(&_money, column->buffer, sizeof _money); - NSNumber* _value = @(_money.mny4); - NSDecimalNumber* decimalNumber = [NSDecimalNumber decimalNumberWithString:[_value description]]; + DBMONEY4 _value; + memcpy(&_value, column->buffer, sizeof _value); + NSNumber* number = @(_value.mny4); + NSDecimalNumber* decimalNumber = [NSDecimalNumber decimalNumberWithString:[number description]]; value = [decimalNumber decimalNumberByDividingBy:[NSDecimalNumber decimalNumberWithString:@"10000"]]; break; } case SYBMONEY: //Monetary data from -922,337,203,685,477.5808 to 922,337,203,685,477.5807 { - BYTE* string = calloc(20, sizeof(BYTE)); //Max string length is 20 - dbconvert(_connection, SYBMONEY, column->buffer, sizeof(SYBMONEY), SYBCHAR, string, -1); - value = [NSDecimalNumber decimalNumberWithString:[NSString stringWithUTF8String:(char*)string]]; - free(string); + BYTE* _value = calloc(20, sizeof(BYTE)); //Max string length is 20 + dbconvert(_connection, SYBMONEY, column->buffer, sizeof(SYBMONEY), SYBCHAR, _value, -1); + value = [NSDecimalNumber decimalNumberWithString:[NSString stringWithUTF8String:(char*)_value]]; + free(_value); break; } case SYBDECIMAL: //Numbers from -10^38 +1 to 10^38 –1. From 5edf98277e80fd90b9ca8eb6a0bff00888c5d081 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 08:57:40 -0400 Subject: [PATCH 030/127] Moved methods and added pragma --- SQLClient/SQLClient/SQLClient/SQLClient.m | 45 ++++++++++++----------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index c09a240..f3cc22d 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -540,6 +540,28 @@ int err_handler(DBPROCESS* dbproc, int severity, int dberr, int oserr, char* dbe return INT_CANCEL; } +//Forwards a message to the delegate on the callback queue if it implements +- (void)message:(NSString*)message +{ + //Invoke delegate on calling queue + [self.callbackQueue addOperationWithBlock:^{ + if ([self.delegate respondsToSelector:@selector(message:)]) { + [self.delegate message:message]; + } + }]; +} + +//Forwards an error message to the delegate on the callback queue. +- (void)error:(NSString*)error code:(int)code severity:(int)severity +{ + //Invoke delegate on callback queue + [self.callbackQueue addOperationWithBlock:^{ + if (![self.delegate conformsToProtocol:@protocol(SQLClientDelegate)]) { + [NSException raise:SQLClientDelegateError format:nil]; + } + [self.delegate error:error code:code severity:severity]; + }]; +} #pragma mark - Cleanup @@ -607,28 +629,7 @@ - (void)executionSuccess:(void (^)(NSArray* results))completion results:(NSArray }]; } -//Forwards a message to the delegate on the callback queue if it implements -- (void)message:(NSString*)message -{ - //Invoke delegate on calling queue - [self.callbackQueue addOperationWithBlock:^{ - if ([self.delegate respondsToSelector:@selector(message:)]) { - [self.delegate message:message]; - } - }]; -} - -//Forwards an error message to the delegate on the callback queue. -- (void)error:(NSString*)error code:(int)code severity:(int)severity -{ - //Invoke delegate on callback queue - [self.callbackQueue addOperationWithBlock:^{ - if (![self.delegate conformsToProtocol:@protocol(SQLClientDelegate)]) { - [NSException raise:SQLClientDelegateError format:nil]; - } - [self.delegate error:error code:code severity:severity]; - }]; -} +#pragma mark - Reference Date //January 1, 1900 - (NSDate*)referenceDate From d745831eaff55f4757b0115fbd3293be3752a061 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 09:08:36 -0400 Subject: [PATCH 031/127] Changed order of things --- SQLClient/SQLClient/SQLClient/SQLClient.m | 36 ++++++++++++----------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index f3cc22d..a2f7afc 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -39,9 +39,9 @@ @interface SQLClient () @implementation SQLClient { + char* _password; LOGINREC* _login; DBPROCESS* _connection; - char* _password; struct COL* _columns; } @@ -121,8 +121,8 @@ - (void)connect:(NSString*)host //Initialize login struct _login = dblogin(); if (_login == FAIL) { - [self cleanupAfterConnection]; [self connectionFailure:completion]; + [self cleanupAfterConnection]; return; } @@ -135,17 +135,19 @@ - (void)connect:(NSString*)host //Connect to database server _connection = dbopen(_login, [self.host UTF8String]); if (!_connection) { - [self cleanupAfterConnection]; [self connectionFailure:completion]; + [self cleanupAfterConnection]; return; } - //Switch to database - RETCODE code = dbuse(_connection, [self.database UTF8String]); - if (code == FAIL) { - [self cleanupAfterConnection]; - [self connectionFailure:completion]; - return; + //Switch to database, if provided + if (self.database) { + RETCODE code = dbuse(_connection, [self.database UTF8String]); + if (code == FAIL) { + [self connectionFailure:completion]; + [self cleanupAfterConnection]; + return; + } } //Success! @@ -174,8 +176,8 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Execute SQL statement if (dbsqlexec(_connection) == FAIL) { - [self cleanupAfterExecution:0]; [self executionFailure:completion]; + [self cleanupAfterExecution:0]; return; } @@ -188,8 +190,8 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion while ((returnCode = dbresults(_connection)) != NO_MORE_RESULTS) { if (returnCode == FAIL) { - [self cleanupAfterExecution:0]; [self executionFailure:completion]; + [self cleanupAfterExecution:0]; return; } @@ -206,8 +208,8 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Allocate C-style array of COL structs _columns = calloc(numColumns, sizeof(struct COL)); if (!_columns) { - [self cleanupAfterExecution:0]; [self executionFailure:completion]; + [self cleanupAfterExecution:0]; return; } @@ -225,8 +227,8 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Create buffer for column data column->buffer = calloc(1, column->size); if (!column->buffer) { - [self cleanupAfterExecution:numColumns]; [self executionFailure:completion]; + [self cleanupAfterExecution:numColumns]; return; } @@ -327,16 +329,16 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Bind column data RETCODE returnCode = dbbind(_connection, c, varType, column->size, column->buffer); if (returnCode == FAIL) { - [self cleanupAfterExecution:numColumns]; [self executionFailure:completion]; + [self cleanupAfterExecution:numColumns]; return; } //Bind null value into column status returnCode = dbnullbind(_connection, c, &column->status); if (returnCode == FAIL) { - [self cleanupAfterExecution:numColumns]; [self executionFailure:completion]; + [self cleanupAfterExecution:numColumns]; return; } @@ -489,13 +491,13 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } //Buffer full case BUF_FULL: - [self cleanupAfterExecution:numColumns]; [self executionFailure:completion]; + [self cleanupAfterExecution:numColumns]; return; //Error case FAIL: - [self cleanupAfterExecution:numColumns]; [self executionFailure:completion]; + [self cleanupAfterExecution:numColumns]; return; default: [self message:SQLClientRowIgnoreMessage]; From d9d02de52100ba08d0a09219f08277860ae4c714 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 15:44:14 -0400 Subject: [PATCH 032/127] Added link to describe GUID format --- SQLClient/SQLClient/SQLClient/SQLClient.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index a2f7afc..1c6139f 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -473,7 +473,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion value = [UIImage imageWithData:data]; break; } - case SYBUNIQUEIDENTIFIER: + case SYBUNIQUEIDENTIFIER: //https://en.wikipedia.org/wiki/Globally_unique_identifier#Binary_encoding { value = [[NSUUID alloc] initWithUUIDBytes:column->buffer]; break; From b86198dd2b6e618433ae477262835e94e85b05a5 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 16:31:29 -0400 Subject: [PATCH 033/127] Changed order --- SQLClient/SQLClient/SQLClient/SQLClient.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 1c6139f..b35d8f1 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -311,8 +311,8 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion varType = DATETIMEBIND; break; } - case SYBBINARY: case SYBVOID: + case SYBBINARY: case SYBVARBINARY: case SYBIMAGE: { From 07f86d41cd383be3937f578afaee8d0626196460 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 16:31:44 -0400 Subject: [PATCH 034/127] Default to NSNull --- SQLClient/SQLClient/SQLClient/SQLClient.m | 224 +++++++++++----------- 1 file changed, 110 insertions(+), 114 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index b35d8f1..8c4942e 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -362,122 +362,118 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Loop through each column and create an entry where dictionary[columnName] = columnValue for (column = _columns; column - _columns < numColumns; column++) { - id value; + //Default to null + id value = [NSNull null]; - //Check for null - if (column->status == -1) { - value = [NSNull null]; - } else { - switch (column->type) + switch (column->type) + { + case SYBBIT: //0 or 1 { - case SYBBIT: //0 or 1 - { - BOOL _value; - memcpy(&_value, column->buffer, sizeof _value); - value = [NSNumber numberWithBool:_value]; - break; - } - case SYBINT1: //Whole numbers from 0 to 255 - case SYBINT2: //Whole numbers between -32,768 and 32,767 - case SYBINT4: //Whole numbers between -2,147,483,648 and 2,147,483,647 - { - int32_t _value; - memcpy(&_value, column->buffer, sizeof _value); - value = [NSNumber numberWithInt:_value]; - break; - } - case SYBINT8: //Whole numbers between -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807 - { - long long _value; - memcpy(&_value, column->buffer, sizeof _value); - value = [NSNumber numberWithLongLong:_value]; - break; - } - case SYBFLT8: //Floating precision number data from -1.79E + 308 to 1.79E + 308 - { - double _value; - memcpy(&_value, column->buffer, sizeof _value); - value = [NSNumber numberWithDouble:_value]; - break; - } - case SYBREAL: //Floating precision number data from -3.40E + 38 to 3.40E + 38 - { - float _value; - memcpy(&_value, column->buffer, sizeof _value); - value = [NSNumber numberWithFloat:_value]; - break; - } - case SYBMONEY4: //Monetary data from -214,748.3648 to 214,748.3647 - { - DBMONEY4 _value; - memcpy(&_value, column->buffer, sizeof _value); - NSNumber* number = @(_value.mny4); - NSDecimalNumber* decimalNumber = [NSDecimalNumber decimalNumberWithString:[number description]]; - value = [decimalNumber decimalNumberByDividingBy:[NSDecimalNumber decimalNumberWithString:@"10000"]]; - break; - } - case SYBMONEY: //Monetary data from -922,337,203,685,477.5808 to 922,337,203,685,477.5807 - { - BYTE* _value = calloc(20, sizeof(BYTE)); //Max string length is 20 - dbconvert(_connection, SYBMONEY, column->buffer, sizeof(SYBMONEY), SYBCHAR, _value, -1); - value = [NSDecimalNumber decimalNumberWithString:[NSString stringWithUTF8String:(char*)_value]]; - free(_value); - break; - } - case SYBDECIMAL: //Numbers from -10^38 +1 to 10^38 –1. - case SYBNUMERIC: - { - NSString* _value = [[NSString alloc] initWithUTF8String:(char*)column->buffer]; - value = [NSDecimalNumber decimalNumberWithString:_value]; - break; - } - case SYBCHAR: - case SYBVARCHAR: - case SYBNVARCHAR: - case SYBTEXT: - case SYBNTEXT: - { - value = [NSString stringWithUTF8String:(char*)column->buffer]; - break; - } - case SYBDATETIME: - case SYBDATETIME4: - case SYBDATETIMN: - case SYBDATE: - case SYBTIME: - case SYBBIGDATETIME: - case SYBBIGTIME: - case SYBMSDATE: - case SYBMSTIME: - case SYBMSDATETIME2: - case SYBMSDATETIMEOFFSET: - { - DBDATETIME _value; - memcpy(&_value, column->buffer, sizeof _value); - NSTimeInterval daysSinceReferenceDate = (NSTimeInterval)_value.dtdays; //Days are counted from 1/1/1900 - NSTimeInterval secondsSinceReferenceDate = daysSinceReferenceDate * 24 * 60 * 60; - NSTimeInterval secondsSinceMidnight = _value.dttime / 3000; //Time is in increments of 3.33 milliseconds - value = [NSDate dateWithTimeInterval:secondsSinceReferenceDate + secondsSinceMidnight sinceDate:[self referenceDate]]; - break; - } - case SYBVOID: - case SYBBINARY: - case SYBVARBINARY: - { - value = [NSData dataWithBytes:column->buffer length:column->size]; - break; - } - case SYBIMAGE: - { - NSData* data = [NSData dataWithBytes:column->buffer length:column->size]; - value = [UIImage imageWithData:data]; - break; - } - case SYBUNIQUEIDENTIFIER: //https://en.wikipedia.org/wiki/Globally_unique_identifier#Binary_encoding - { - value = [[NSUUID alloc] initWithUUIDBytes:column->buffer]; - break; - } + BOOL _value; + memcpy(&_value, column->buffer, sizeof _value); + value = [NSNumber numberWithBool:_value]; + break; + } + case SYBINT1: //Whole numbers from 0 to 255 + case SYBINT2: //Whole numbers between -32,768 and 32,767 + case SYBINT4: //Whole numbers between -2,147,483,648 and 2,147,483,647 + { + int32_t _value; + memcpy(&_value, column->buffer, sizeof _value); + value = [NSNumber numberWithInt:_value]; + break; + } + case SYBINT8: //Whole numbers between -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807 + { + long long _value; + memcpy(&_value, column->buffer, sizeof _value); + value = [NSNumber numberWithLongLong:_value]; + break; + } + case SYBFLT8: //Floating precision number data from -1.79E + 308 to 1.79E + 308 + { + double _value; + memcpy(&_value, column->buffer, sizeof _value); + value = [NSNumber numberWithDouble:_value]; + break; + } + case SYBREAL: //Floating precision number data from -3.40E + 38 to 3.40E + 38 + { + float _value; + memcpy(&_value, column->buffer, sizeof _value); + value = [NSNumber numberWithFloat:_value]; + break; + } + case SYBMONEY4: //Monetary data from -214,748.3648 to 214,748.3647 + { + DBMONEY4 _value; + memcpy(&_value, column->buffer, sizeof _value); + NSNumber* number = @(_value.mny4); + NSDecimalNumber* decimalNumber = [NSDecimalNumber decimalNumberWithString:[number description]]; + value = [decimalNumber decimalNumberByDividingBy:[NSDecimalNumber decimalNumberWithString:@"10000"]]; + break; + } + case SYBMONEY: //Monetary data from -922,337,203,685,477.5808 to 922,337,203,685,477.5807 + { + BYTE* _value = calloc(20, sizeof(BYTE)); //Max string length is 20 + dbconvert(_connection, SYBMONEY, column->buffer, sizeof(SYBMONEY), SYBCHAR, _value, -1); + value = [NSDecimalNumber decimalNumberWithString:[NSString stringWithUTF8String:(char*)_value]]; + free(_value); + break; + } + case SYBDECIMAL: //Numbers from -10^38 +1 to 10^38 –1. + case SYBNUMERIC: + { + NSString* _value = [[NSString alloc] initWithUTF8String:(char*)column->buffer]; + value = [NSDecimalNumber decimalNumberWithString:_value]; + break; + } + case SYBCHAR: + case SYBVARCHAR: + case SYBNVARCHAR: + case SYBTEXT: + case SYBNTEXT: + { + value = [NSString stringWithUTF8String:(char*)column->buffer]; + break; + } + case SYBDATETIME: + case SYBDATETIME4: + case SYBDATETIMN: + case SYBDATE: + case SYBTIME: + case SYBBIGDATETIME: + case SYBBIGTIME: + case SYBMSDATE: + case SYBMSTIME: + case SYBMSDATETIME2: + case SYBMSDATETIMEOFFSET: + { + DBDATETIME _value; + memcpy(&_value, column->buffer, sizeof _value); + NSTimeInterval daysSinceReferenceDate = (NSTimeInterval)_value.dtdays; //Days are counted from 1/1/1900 + NSTimeInterval secondsSinceReferenceDate = daysSinceReferenceDate * 24 * 60 * 60; + NSTimeInterval secondsSinceMidnight = _value.dttime / 3000; //Time is in increments of 3.33 milliseconds + value = [NSDate dateWithTimeInterval:secondsSinceReferenceDate + secondsSinceMidnight sinceDate:[self referenceDate]]; + break; + } + case SYBVOID: + case SYBBINARY: + case SYBVARBINARY: + { + value = [NSData dataWithBytes:column->buffer length:column->size]; + break; + } + case SYBIMAGE: + { + NSData* data = [NSData dataWithBytes:column->buffer length:column->size]; + value = [UIImage imageWithData:data]; + break; + } + case SYBUNIQUEIDENTIFIER: //https://en.wikipedia.org/wiki/Globally_unique_identifier#Binary_encoding + { + value = [[NSUUID alloc] initWithUUIDBytes:column->buffer]; + break; } } From ce8ccf5bc2a22bb412c33c142f11c90e1a83adb5 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 16:38:35 -0400 Subject: [PATCH 035/127] Removed logging statements --- SQLClient/SQLClient/SQLClient/SQLClient.m | 4 ---- 1 file changed, 4 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 8c4942e..c923100 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -341,12 +341,8 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion [self cleanupAfterExecution:numColumns]; return; } - - //printf("%s is type %d with value %s\n", pcol->name, pcol->type, pcol->buffer); } - //printf("\n"); - //Loop through each row while ((rowCode = dbnextrow(_connection)) != NO_MORE_ROWS) { From b2e3d214771dfd33fb095cff7a7ffaf1d09d9934 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 16:38:56 -0400 Subject: [PATCH 036/127] Clarified date types that are identified as SYBCHAR --- SQLClient/SQLClient/SQLClient/SQLClient.m | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index c923100..721ea80 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -299,13 +299,14 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBDATETIME: case SYBDATETIME4: case SYBDATETIMN: -// case SYBDATE: - case SYBTIME: case SYBBIGDATETIME: case SYBBIGTIME: -// case SYBMSDATE: + //FreeTDS incorrectly identifies the following types as SYBCHAR: + case SYBDATE: + case SYBTIME: + case SYBMSDATE: case SYBMSTIME: -// case SYBMSDATETIME2: + case SYBMSDATETIME2: case SYBMSDATETIMEOFFSET: { varType = DATETIMEBIND; From ba1394a2facb4aa7c2d81612b0d3f30a8812525e Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 16:42:31 -0400 Subject: [PATCH 037/127] Removed unsupported date types --- SQLClient/SQLClient/SQLClient/SQLClient.m | 6 ------ 1 file changed, 6 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 721ea80..1ee8290 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -437,14 +437,8 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBDATETIME: case SYBDATETIME4: case SYBDATETIMN: - case SYBDATE: - case SYBTIME: case SYBBIGDATETIME: case SYBBIGTIME: - case SYBMSDATE: - case SYBMSTIME: - case SYBMSDATETIME2: - case SYBMSDATETIMEOFFSET: { DBDATETIME _value; memcpy(&_value, column->buffer, sizeof _value); From 6e7c7bd12f6513519715f6dc1cd2d661c6d391bc Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 16:50:47 -0400 Subject: [PATCH 038/127] Updated readme with new data types --- README.md | 89 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 27bd1c8..160e4b2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ SQLClient ========= -Native Microsoft SQL Server client for iOS. An Objective-C wrapper around the open-source FreeTDS library. +Native Microsoft SQL Server client for iOS. An Objective-C wrapper around the open-source [FreeTDS](https://github.com/FreeTDS/freetds/) library. ##Sample Usage @@ -11,8 +11,7 @@ Native Microsoft SQL Server client for iOS. An Objective-C wrapper around the op SQLClient* client = [SQLClient sharedInstance]; client.delegate = self; [client connect:@"server:port" username:@"user" password:@"pass" database:@"db" completion:^(BOOL success) { - if (success) - { + if (success) { [client execute:@"SELECT * FROM Users" completion:^(NSArray* results) { for (NSArray* table in results) { for (NSDictionary* row in table) { @@ -34,57 +33,61 @@ client.delegate = self; ##Type Conversion - -* bigint -> NSNumber -* binary(n) -> NSData -* bit -> NSNumber -* char(n) -> NSString -* cursor ? -* date -> **NSString** -* datetime -> NSDate -* datetime2 -> **NSString** -* datetimeoffset -> **NSString** -* decimal(p,s) -> NSNumber -* float(n) -> NSNumber -* image -> UIImage -* int -> NSNumber -* money -> NSDecimalNumber **(last 2 digits are truncated)** -* nchar -> NSString -* ntext -> NSString -* numeric(p,s) -> NSNumber -* nvarchar -> NSString -* nvarchar(max) -> NSString -* real -> NSNumber -* smalldatetime -> NSDate -* smallint -> NSNumber -* smallmoney -> NSDecimalNumber -* sql_variant -> ? -* table -> ? -* text -> NSString -* time -> **NSString** -* timestamp -> ? -* tinyint -> NSNumber -* uniqueidentifier -> NSUUID -* varbinary -> NSData -* varbinary(max) -> NSData -* varchar(max) -> NSString -* varchar(n) -> NSString -* xml -> +SQLClient maps SQL Server data types into the following native Objective-C objects: + +* bigint → NSNumber +* binary(n) → NSData +* bit → NSNumber +* char(n) → NSString +* cursor → **not supported** +* date → **NSString** +* datetime → NSDate +* datetime2 → **NSString** +* datetimeoffset → **NSString** +* decimal(p,s) → NSNumber +* float(n) → NSNumber +* image → UIImage +* int → NSNumber +* money → NSDecimalNumber **(last 2 digits are truncated)** +* nchar → NSString +* ntext → NSString +* numeric(p,s) → NSNumber +* nvarchar → NSString +* nvarchar(max) → NSString +* real → NSNumber +* smalldatetime → NSDate +* smallint → NSNumber +* smallmoney → NSDecimalNumber +* sql_variant → **not supported** +* table → **not supported** +* text → NSString +* time → **NSString** +* timestamp → **NSData** +* tinyint → NSNumber +* uniqueidentifier → NSUUID +* varbinary → NSData +* varbinary(max) → NSData +* varchar(max) → NSString +* varchar(n) → NSString +* xml → NSString ##Testing The type conversions have been tested with SQL Server 2008 R2. ## Known Issues +PR's welcome! * **money**: FreeTDS will truncate the rightmost 2 digits. - -* The following data types are recognized by FreeTDS as type **47 (SYBCHAR)**, so SQLClient can't convert them into proper objects: - +* The following data types are recognized by FreeTDS as type **SYBCHAR**, so SQLClient can't convert them into proper **NSDate** objects: + * date * datetime2 - * date * datetimeoffset * time +* OSX support: [FreeTDS-iOS](https://github.com/martinrybak/FreeTDS-iOS) needs to be compiled to support OSX, Podspec updated +* No support for stored procedures with out parameters (yet) +* No support for returning number of rows changed (yet) +* Swift bindings: I welcome a PR to make the API more Swift-friendly ##Demo Project From ce8b0d1019b1648494c7f1e990121dc9ed79c976 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 17:06:36 -0400 Subject: [PATCH 039/127] Consolidated options --- SQLClient/SQLClient/SQLClient/SQLClient.m | 4 ---- 1 file changed, 4 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 1ee8290..2ca7613 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -316,10 +316,6 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBBINARY: case SYBVARBINARY: case SYBIMAGE: - { - varType = BINARYBIND; - break; - } case SYBUNIQUEIDENTIFIER: { varType = BINARYBIND; From a4bf3ad0ae81bed96e9a8413bad9228529e523cb Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 17:06:48 -0400 Subject: [PATCH 040/127] Fixed cleanup code --- SQLClient/SQLClient/SQLClient/SQLClient.m | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 2ca7613..dc1553d 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -553,22 +553,19 @@ - (void)error:(NSString*)error code:(int)code severity:(int)severity - (void)cleanupAfterConnection { dbloginfree(_login); - if (_password) { - free(_password); - } + free(_password); + _password = NULL; } - (void)cleanupAfterExecution:(int)numColumns { struct COL* column; for (column = _columns; column - _columns < numColumns; column++) { - if (column->buffer) { - free(column->buffer); - } - } - if (_columns) { - free(_columns); + free(column->buffer); + column->buffer = NULL; } + free(_columns); + _columns = NULL; dbfreebuf(_connection); } From f7a2e2ee78f2f225327dc1e042606ae9ec066cd2 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 17:31:28 -0400 Subject: [PATCH 041/127] If not null, update value with column data --- SQLClient/SQLClient/SQLClient/SQLClient.m | 206 +++++++++++----------- 1 file changed, 105 insertions(+), 101 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index dc1553d..759b146 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -358,109 +358,113 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Default to null id value = [NSNull null]; - switch (column->type) + //If not null, update value with column data + if (column->status != -1) { - case SYBBIT: //0 or 1 + switch (column->type) { - BOOL _value; - memcpy(&_value, column->buffer, sizeof _value); - value = [NSNumber numberWithBool:_value]; - break; - } - case SYBINT1: //Whole numbers from 0 to 255 - case SYBINT2: //Whole numbers between -32,768 and 32,767 - case SYBINT4: //Whole numbers between -2,147,483,648 and 2,147,483,647 - { - int32_t _value; - memcpy(&_value, column->buffer, sizeof _value); - value = [NSNumber numberWithInt:_value]; - break; - } - case SYBINT8: //Whole numbers between -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807 - { - long long _value; - memcpy(&_value, column->buffer, sizeof _value); - value = [NSNumber numberWithLongLong:_value]; - break; - } - case SYBFLT8: //Floating precision number data from -1.79E + 308 to 1.79E + 308 - { - double _value; - memcpy(&_value, column->buffer, sizeof _value); - value = [NSNumber numberWithDouble:_value]; - break; - } - case SYBREAL: //Floating precision number data from -3.40E + 38 to 3.40E + 38 - { - float _value; - memcpy(&_value, column->buffer, sizeof _value); - value = [NSNumber numberWithFloat:_value]; - break; - } - case SYBMONEY4: //Monetary data from -214,748.3648 to 214,748.3647 - { - DBMONEY4 _value; - memcpy(&_value, column->buffer, sizeof _value); - NSNumber* number = @(_value.mny4); - NSDecimalNumber* decimalNumber = [NSDecimalNumber decimalNumberWithString:[number description]]; - value = [decimalNumber decimalNumberByDividingBy:[NSDecimalNumber decimalNumberWithString:@"10000"]]; - break; - } - case SYBMONEY: //Monetary data from -922,337,203,685,477.5808 to 922,337,203,685,477.5807 - { - BYTE* _value = calloc(20, sizeof(BYTE)); //Max string length is 20 - dbconvert(_connection, SYBMONEY, column->buffer, sizeof(SYBMONEY), SYBCHAR, _value, -1); - value = [NSDecimalNumber decimalNumberWithString:[NSString stringWithUTF8String:(char*)_value]]; - free(_value); - break; - } - case SYBDECIMAL: //Numbers from -10^38 +1 to 10^38 –1. - case SYBNUMERIC: - { - NSString* _value = [[NSString alloc] initWithUTF8String:(char*)column->buffer]; - value = [NSDecimalNumber decimalNumberWithString:_value]; - break; - } - case SYBCHAR: - case SYBVARCHAR: - case SYBNVARCHAR: - case SYBTEXT: - case SYBNTEXT: - { - value = [NSString stringWithUTF8String:(char*)column->buffer]; - break; - } - case SYBDATETIME: - case SYBDATETIME4: - case SYBDATETIMN: - case SYBBIGDATETIME: - case SYBBIGTIME: - { - DBDATETIME _value; - memcpy(&_value, column->buffer, sizeof _value); - NSTimeInterval daysSinceReferenceDate = (NSTimeInterval)_value.dtdays; //Days are counted from 1/1/1900 - NSTimeInterval secondsSinceReferenceDate = daysSinceReferenceDate * 24 * 60 * 60; - NSTimeInterval secondsSinceMidnight = _value.dttime / 3000; //Time is in increments of 3.33 milliseconds - value = [NSDate dateWithTimeInterval:secondsSinceReferenceDate + secondsSinceMidnight sinceDate:[self referenceDate]]; - break; - } - case SYBVOID: - case SYBBINARY: - case SYBVARBINARY: - { - value = [NSData dataWithBytes:column->buffer length:column->size]; - break; - } - case SYBIMAGE: - { - NSData* data = [NSData dataWithBytes:column->buffer length:column->size]; - value = [UIImage imageWithData:data]; - break; - } - case SYBUNIQUEIDENTIFIER: //https://en.wikipedia.org/wiki/Globally_unique_identifier#Binary_encoding - { - value = [[NSUUID alloc] initWithUUIDBytes:column->buffer]; - break; + case SYBBIT: //0 or 1 + { + BOOL _value; + memcpy(&_value, column->buffer, sizeof _value); + value = [NSNumber numberWithBool:_value]; + break; + } + case SYBINT1: //Whole numbers from 0 to 255 + case SYBINT2: //Whole numbers between -32,768 and 32,767 + case SYBINT4: //Whole numbers between -2,147,483,648 and 2,147,483,647 + { + int32_t _value; + memcpy(&_value, column->buffer, sizeof _value); + value = [NSNumber numberWithInt:_value]; + break; + } + case SYBINT8: //Whole numbers between -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807 + { + long long _value; + memcpy(&_value, column->buffer, sizeof _value); + value = [NSNumber numberWithLongLong:_value]; + break; + } + case SYBFLT8: //Floating precision number data from -1.79E + 308 to 1.79E + 308 + { + double _value; + memcpy(&_value, column->buffer, sizeof _value); + value = [NSNumber numberWithDouble:_value]; + break; + } + case SYBREAL: //Floating precision number data from -3.40E + 38 to 3.40E + 38 + { + float _value; + memcpy(&_value, column->buffer, sizeof _value); + value = [NSNumber numberWithFloat:_value]; + break; + } + case SYBMONEY4: //Monetary data from -214,748.3648 to 214,748.3647 + { + DBMONEY4 _value; + memcpy(&_value, column->buffer, sizeof _value); + NSNumber* number = @(_value.mny4); + NSDecimalNumber* decimalNumber = [NSDecimalNumber decimalNumberWithString:[number description]]; + value = [decimalNumber decimalNumberByDividingBy:[NSDecimalNumber decimalNumberWithString:@"10000"]]; + break; + } + case SYBMONEY: //Monetary data from -922,337,203,685,477.5808 to 922,337,203,685,477.5807 + { + BYTE* _value = calloc(20, sizeof(BYTE)); //Max string length is 20 + dbconvert(_connection, SYBMONEY, column->buffer, sizeof(SYBMONEY), SYBCHAR, _value, -1); + value = [NSDecimalNumber decimalNumberWithString:[NSString stringWithUTF8String:(char*)_value]]; + free(_value); + break; + } + case SYBDECIMAL: //Numbers from -10^38 +1 to 10^38 –1. + case SYBNUMERIC: + { + NSString* _value = [[NSString alloc] initWithUTF8String:(char*)column->buffer]; + value = [NSDecimalNumber decimalNumberWithString:_value]; + break; + } + case SYBCHAR: + case SYBVARCHAR: + case SYBNVARCHAR: + case SYBTEXT: + case SYBNTEXT: + { + value = [NSString stringWithUTF8String:(char*)column->buffer]; + break; + } + case SYBDATETIME: + case SYBDATETIME4: + case SYBDATETIMN: + case SYBBIGDATETIME: + case SYBBIGTIME: + { + DBDATETIME _value; + memcpy(&_value, column->buffer, sizeof _value); + NSTimeInterval daysSinceReferenceDate = (NSTimeInterval)_value.dtdays; //Days are counted from 1/1/1900 + NSTimeInterval secondsSinceReferenceDate = daysSinceReferenceDate * 24 * 60 * 60; + NSTimeInterval secondsSinceMidnight = _value.dttime / 3000; //Time is in increments of 3.33 milliseconds + value = [NSDate dateWithTimeInterval:secondsSinceReferenceDate + secondsSinceMidnight sinceDate:[self referenceDate]]; + break; + } + case SYBVOID: + case SYBBINARY: + case SYBVARBINARY: + { + value = [NSData dataWithBytes:column->buffer length:column->size]; + break; + } + case SYBIMAGE: + { + NSData* data = [NSData dataWithBytes:column->buffer length:column->size]; + value = [UIImage imageWithData:data]; + break; + } + case SYBUNIQUEIDENTIFIER: //https://en.wikipedia.org/wiki/Globally_unique_identifier#Binary_encoding + { + value = [[NSUUID alloc] initWithUUIDBytes:column->buffer]; + break; + } } } From db87d73699707c519bb390c7b8c4b8d36320eb6c Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 21:43:50 -0400 Subject: [PATCH 042/127] Better concurrency handling --- SQLClient/SQLClient/SQLClient/SQLClient.h | 13 ++-- SQLClient/SQLClient/SQLClient/SQLClient.m | 84 ++++++++++++++++------- 2 files changed, 68 insertions(+), 29 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.h b/SQLClient/SQLClient/SQLClient/SQLClient.h index 77e40d0..01ea2a3 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.h +++ b/SQLClient/SQLClient/SQLClient/SQLClient.h @@ -43,17 +43,17 @@ /** * The database server, i.e. server, server:port, or server\instance (be sure to escape the backslash) */ -@property (nonatomic, copy, readonly) NSString* host; +@property (atomic, copy, readonly) NSString* host; /** * The database username */ -@property (nonatomic, copy, readonly) NSString* username; +@property (atomic, copy, readonly) NSString* username; /** * The database name to use */ -@property (nonatomic, copy, readonly) NSString* database; +@property (atomic, copy, readonly) NSString* database; /** * The delegate to receive error: and message: callbacks @@ -102,10 +102,15 @@ completion:(void (^)(BOOL success))completion; /** - * Indicates whether the database is currently connected + * Indicates whether the database is currently connected. */ - (BOOL)isConnected; +/** + * Indicates whether the database is executing a command. + */ +- (BOOL)isExecuting; + /** * Executes a SQL statement. Results of queries will be passed to the completion handler. Inserts, updates, and deletes do not return results. * diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 759b146..ab2a365 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -18,6 +18,9 @@ NSString* const SQLClientDefaultCharset = @"UTF-8"; NSString* const SQLClientWorkerQueueName = @"com.martinrybak.sqlclient"; NSString* const SQLClientDelegateError = @"Delegate must be set to an NSObject that implements the SQLClientDelegate protocol"; +NSString* const SQLClientPendingConnectionError = @"Attempting to connect while a connection is active."; +NSString* const SQLClientNoConnectionError = @"Attempting to execute while not connected."; +NSString* const SQLClientPendingExecutionError = @"Attempting to execute while a command is in progress."; NSString* const SQLClientRowIgnoreMessage = @"Ignoring unknown row type"; struct COL @@ -31,9 +34,10 @@ @interface SQLClient () -@property (nonatomic, copy, readwrite) NSString* host; -@property (nonatomic, copy, readwrite) NSString* username; -@property (nonatomic, copy, readwrite) NSString* database; +@property (atomic, copy, readwrite) NSString* host; +@property (atomic, copy, readwrite) NSString* username; +@property (atomic, copy, readwrite) NSString* database; +@property (atomic, assign, getter=isExecuting) BOOL executing; @end @@ -63,6 +67,8 @@ - (id)init self.callbackQueue = [NSOperationQueue currentQueue]; self.workerQueue = [[NSOperationQueue alloc] init]; self.workerQueue.name = SQLClientWorkerQueueName; + self.workerQueue.maxConcurrentOperationCount = 1; + self.executing = NO; //Set FreeTDS callback handlers dberrhandle(err_handler); @@ -95,26 +101,33 @@ - (void)connect:(NSString*)host database:(NSString*)database completion:(void (^)(BOOL success))completion { - //Save inputs - self.host = host; - self.username = username; - self.database = database; - - /* - Copy password into a global C string. This is because in connectionSuccess: and connectionFailure:, - dbloginfree() will attempt to overwrite the password in the login struct with zeroes for security. - So it must be a string that stays alive until then. Passing in [password UTF8String] does not work because: - - "The returned C string is a pointer to a structure inside the string object, which may have a lifetime - shorter than the string object and will certainly not have a longer lifetime. Therefore, you should - copy the C string if it needs to be stored outside of the memory context in which you called this method." - https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/occ/instm/NSString/UTF8String - */ - _password = strdup([password UTF8String]); - //Connect to database on worker queue [self.workerQueue addOperationWithBlock:^{ + if (self.isConnected) { + [self message:SQLClientPendingConnectionError]; + [self connectionFailure:completion]; + [self cleanupAfterConnection]; + return; + } + + //Save inputs + self.host = host; + self.username = username; + self.database = database; + + /* + Copy password into a global C string. This is because in connectionSuccess: and connectionFailure:, + dbloginfree() will attempt to overwrite the password in the login struct with zeroes for security. + So it must be a string that stays alive until then. Passing in [password UTF8String] does not work because: + + "The returned C string is a pointer to a structure inside the string object, which may have a lifetime + shorter than the string object and will certainly not have a longer lifetime. Therefore, you should + copy the C string if it needs to be stored outside of the memory context in which you called this method." + https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/occ/instm/NSString/UTF8String + */ + _password = strdup([password UTF8String]); + //Set login timeout dbsetlogintime(self.timeout); @@ -168,6 +181,22 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Execute query on worker queue [self.workerQueue addOperationWithBlock:^{ + if (!self.isConnected) { + [self message:SQLClientNoConnectionError]; + [self executionFailure:completion]; + [self cleanupAfterExecution:0]; + return; + } + + if (self.isExecuting) { + [self message:SQLClientPendingExecutionError]; + [self executionFailure:completion]; + [self cleanupAfterExecution:0]; + return; + } + + self.executing = YES; + //Set query timeout dbsettime(self.timeout); @@ -184,6 +213,9 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Create array to contain the tables NSMutableArray* output = [NSMutableArray array]; + //Get number of columns + int numColumns = dbnumcols(_connection); + //Loop through each table metadata //dbresults() returns SUCCEED, FAIL or, NO_MORE_RESULTS. RETCODE returnCode; @@ -195,16 +227,12 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion return; } - int numColumns; struct COL* column; STATUS rowCode; //Create array to contain the rows for this table NSMutableArray* table = [NSMutableArray array]; - //Get number of columns - numColumns = dbnumcols(_connection); - //Allocate C-style array of COL structs _columns = calloc(numColumns, sizeof(struct COL)); if (!_columns) { @@ -494,18 +522,21 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Add immutable copy of table to output [output addObject:[table copy]]; - [self cleanupAfterExecution:numColumns]; } //Success! Send an immutable copy of the results array [self executionSuccess:completion results:[output copy]]; + [self cleanupAfterExecution:numColumns]; }]; } - (void)disconnect { [self.workerQueue addOperationWithBlock:^{ + [self cleanupAfterExecution:0]; + [self cleanupAfterConnection]; dbclose(_connection); + _connection = NULL; }]; } @@ -558,6 +589,7 @@ - (void)cleanupAfterConnection { dbloginfree(_login); free(_password); + _login = NULL; _password = NULL; } @@ -598,6 +630,7 @@ - (void)connectionSuccess:(void (^)(BOOL success))completion //Invokes execution completion handler on callback queue with results = nil - (void)executionFailure:(void (^)(NSArray* results))completion { + self.executing = NO; [self.callbackQueue addOperationWithBlock:^{ if (completion) { completion(nil); @@ -608,6 +641,7 @@ - (void)executionFailure:(void (^)(NSArray* results))completion //Invokes execution completion handler on callback queue with results array - (void)executionSuccess:(void (^)(NSArray* results))completion results:(NSArray*)results { + self.executing = NO; [self.callbackQueue addOperationWithBlock:^{ if (completion) { completion(results); From 3612ff58dbc58c4f87d3ee2e477878dc5fdf0e59 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 21:44:01 -0400 Subject: [PATCH 043/127] Updated comments --- SQLClient/SQLClient/SQLClient/SQLClient.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index ab2a365..c40ed3a 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -174,8 +174,8 @@ - (BOOL)isConnected return !dbdead(_connection); } -// TODO: how to get number of records changed during update or delete -// TODO: how to handle SQL stored procedure output parameters +// TODO: get number of records changed during update or delete +// TODO: handle SQL stored procedure output parameters - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion { //Execute query on worker queue @@ -414,14 +414,14 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion value = [NSNumber numberWithLongLong:_value]; break; } - case SYBFLT8: //Floating precision number data from -1.79E + 308 to 1.79E + 308 + case SYBFLT8: //Floating precision number data from -1.79E+308 to 1.79E+308 { double _value; memcpy(&_value, column->buffer, sizeof _value); value = [NSNumber numberWithDouble:_value]; break; } - case SYBREAL: //Floating precision number data from -3.40E + 38 to 3.40E + 38 + case SYBREAL: //Floating precision number data from -3.40E+38 to 3.40E+38 { float _value; memcpy(&_value, column->buffer, sizeof _value); From 5aa5a3d3ec9162a7d6f19a0990dae835985a8318 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 22:12:43 -0400 Subject: [PATCH 044/127] Updated handling of small and tiny int --- SQLClient/SQLClient/SQLClient/SQLClient.m | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index c40ed3a..8914bf8 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -400,6 +400,12 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } case SYBINT1: //Whole numbers from 0 to 255 case SYBINT2: //Whole numbers between -32,768 and 32,767 + { + int16_t _value; + memcpy(&_value, column->buffer, sizeof _value); + value = [NSNumber numberWithShort:_value]; + break; + } case SYBINT4: //Whole numbers between -2,147,483,648 and 2,147,483,647 { int32_t _value; From 221e377736aafe66070c8ca8cfa9f30ae8ee48fb Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 22:13:20 -0400 Subject: [PATCH 045/127] Added conversion tests for integer types --- SQLClient/SQLClientTests/SQLClientTests.m | 87 ++++++++++++++++++++--- 1 file changed, 79 insertions(+), 8 deletions(-) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index 6f12f28..3e5a897 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -7,6 +7,7 @@ // #import +#import "SQLClient.h" @interface SQLClientTests : XCTestCase @@ -14,21 +15,91 @@ @interface SQLClientTests : XCTestCase @implementation SQLClientTests -- (void)setUp +- (void)testBit { - [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT Bit FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Bit"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"Bit"], @(1)); + XCTAssertEqualObjects(results[0][2][@"Bit"], @(0)); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } -- (void)tearDown +- (void)testTinyInt { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT TinyInt FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"TinyInt"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"TinyInt"], @(0)); + XCTAssertEqualObjects(results[0][2][@"TinyInt"], @(255)); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } -- (void)testExample +- (void)testSmallInt { - XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT SmallInt FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"SmallInt"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"SmallInt"], @(-32768)); + XCTAssertEqualObjects(results[0][2][@"SmallInt"], @(32767)); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testInt +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT Int FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Int"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"Int"], @(-2147483648)); + XCTAssertEqualObjects(results[0][2][@"Int"], @(2147483647)); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testBigInt +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT BigInt FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"BigInt"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"BigInt"], @((signed long long)-9223372036854775808)); + XCTAssertEqualObjects(results[0][2][@"BigInt"], @((signed long long)9223372036854775807)); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +#pragma mark - Private + +- (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion +{ + NSDictionary* environment = [[NSProcessInfo processInfo] environment]; + SQLClient* client = [SQLClient sharedInstance]; + + //Create environment variables in the Test Debug Scheme + NSString* host = environment[@"HOST"]; + NSString* username = environment[@"USERNAME"]; + NSString* password = environment[@"PASSWORD"]; + NSString* database = environment[@"DATABASE"]; + + NSParameterAssert(host); + NSParameterAssert(username); + NSParameterAssert(password); + + [client connect:host username:username password:password database:database completion:^(BOOL success) { + [client execute:sql completion:^(NSArray* results) { + [client disconnect]; + if (completion) { + completion(results); + } + }]; + }]; } @end From acf95b01d3e9bd75d033a4c697bf329ae8276c5b Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 22:25:23 -0400 Subject: [PATCH 046/127] Added checks when using _connection as parameter --- SQLClient/SQLClient/SQLClient/SQLClient.m | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 8914bf8..7d21c44 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -541,8 +541,10 @@ - (void)disconnect [self.workerQueue addOperationWithBlock:^{ [self cleanupAfterExecution:0]; [self cleanupAfterConnection]; - dbclose(_connection); - _connection = NULL; + if (_connection) { + dbclose(_connection); + _connection = NULL; + } }]; } @@ -608,7 +610,9 @@ - (void)cleanupAfterExecution:(int)numColumns } free(_columns); _columns = NULL; - dbfreebuf(_connection); + if (_connection) { + dbfreebuf(_connection); + } } #pragma mark - Private From 0f0bdadb5b6db936f35ee44ae74d2237f389dbe1 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 22:26:21 -0400 Subject: [PATCH 047/127] Added retcode check when adding command to buffer --- SQLClient/SQLClient/SQLClient/SQLClient.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 7d21c44..72d3a3c 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -201,7 +201,11 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion dbsettime(self.timeout); //Prepare SQL statement - dbcmd(_connection, [sql UTF8String]); + if (dbcmd(_connection, [sql UTF8String]) == FAIL) { + [self executionFailure:completion]; + [self cleanupAfterExecution:0]; + return; + } //Execute SQL statement if (dbsqlexec(_connection) == FAIL) { From da9c5660b9dc560dc40898b1d3b94b3335d83c48 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 22:38:47 -0400 Subject: [PATCH 048/127] Replaced exception with parameter assert --- SQLClient/SQLClient/SQLClient/SQLClient.m | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 72d3a3c..85a1a96 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -586,11 +586,9 @@ - (void)message:(NSString*)message //Forwards an error message to the delegate on the callback queue. - (void)error:(NSString*)error code:(int)code severity:(int)severity { + NSParameterAssert([self.delegate conformsToProtocol:@protocol(SQLClientDelegate)]); //Invoke delegate on callback queue [self.callbackQueue addOperationWithBlock:^{ - if (![self.delegate conformsToProtocol:@protocol(SQLClientDelegate)]) { - [NSException raise:SQLClientDelegateError format:nil]; - } [self.delegate error:error code:code severity:severity]; }]; } From 12fef0e1d0b1db267d9263f1c77613cf6dada66b Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 22:49:50 -0400 Subject: [PATCH 049/127] Removed unused constant --- SQLClient/SQLClient/SQLClient/SQLClient.m | 1 - 1 file changed, 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 85a1a96..95e5e4a 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -14,7 +14,6 @@ #define SYBUNIQUEIDENTIFIER 36 int const SQLClientDefaultTimeout = 5; -int const SQLClientDefaultQueryTimeout = 5; NSString* const SQLClientDefaultCharset = @"UTF-8"; NSString* const SQLClientWorkerQueueName = @"com.martinrybak.sqlclient"; NSString* const SQLClientDelegateError = @"Delegate must be set to an NSObject that implements the SQLClientDelegate protocol"; From 97385d6101c2029f08bb95db8e7f7f69313eeb39 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 22:50:10 -0400 Subject: [PATCH 050/127] Renamed COL struct --- SQLClient/SQLClient/SQLClient/SQLClient.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 95e5e4a..11c8cef 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -22,7 +22,7 @@ NSString* const SQLClientPendingExecutionError = @"Attempting to execute while a command is in progress."; NSString* const SQLClientRowIgnoreMessage = @"Ignoring unknown row type"; -struct COL +struct COLUMN { char* name; BYTE* buffer; @@ -45,7 +45,7 @@ @implementation SQLClient char* _password; LOGINREC* _login; DBPROCESS* _connection; - struct COL* _columns; + struct COLUMN* _columns; } #pragma mark - NSObject @@ -230,14 +230,14 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion return; } - struct COL* column; + struct COLUMN* column; STATUS rowCode; //Create array to contain the rows for this table NSMutableArray* table = [NSMutableArray array]; //Allocate C-style array of COL structs - _columns = calloc(numColumns, sizeof(struct COL)); + _columns = calloc(numColumns, sizeof(struct COLUMN)); if (!_columns) { [self executionFailure:completion]; [self cleanupAfterExecution:0]; @@ -604,7 +604,7 @@ - (void)cleanupAfterConnection - (void)cleanupAfterExecution:(int)numColumns { - struct COL* column; + struct COLUMN* column; for (column = _columns; column - _columns < numColumns; column++) { free(column->buffer); column->buffer = NULL; From c59d34118f05af56ae8b86ec91352b3ae0951ff0 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 24 Oct 2016 22:50:25 -0400 Subject: [PATCH 051/127] Fixed compiler warning --- SQLClient/SQLClientTests/SQLClientTests.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index 3e5a897..d5c0741 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -68,8 +68,8 @@ - (void)testBigInt XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:@"SELECT BigInt FROM Test" completion:^(NSArray* results) { XCTAssertEqualObjects(results[0][0][@"BigInt"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"BigInt"], @((signed long long)-9223372036854775808)); - XCTAssertEqualObjects(results[0][2][@"BigInt"], @((signed long long)9223372036854775807)); + XCTAssertEqualObjects(results[0][1][@"BigInt"], @(-9223372036854775807 - 1)); + XCTAssertEqualObjects(results[0][2][@"BigInt"], @(9223372036854775807)); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; From 5f2b19a3884a9250f68cd3db4d7ce7cb0503716f Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 06:38:08 -0400 Subject: [PATCH 052/127] Renamed buffer to data --- SQLClient/SQLClient/SQLClient/SQLClient.m | 38 +++++++++++------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 11c8cef..6916690 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -25,10 +25,10 @@ struct COLUMN { char* name; - BYTE* buffer; int type; int size; int status; + BYTE* data; }; @interface SQLClient () @@ -256,8 +256,8 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion column->size = dbcollen(_connection, c); //Create buffer for column data - column->buffer = calloc(1, column->size); - if (!column->buffer) { + column->data = calloc(1, column->size); + if (!column->data) { [self executionFailure:completion]; [self cleanupAfterExecution:numColumns]; return; @@ -397,7 +397,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBBIT: //0 or 1 { BOOL _value; - memcpy(&_value, column->buffer, sizeof _value); + memcpy(&_value, column->data, sizeof _value); value = [NSNumber numberWithBool:_value]; break; } @@ -405,42 +405,42 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBINT2: //Whole numbers between -32,768 and 32,767 { int16_t _value; - memcpy(&_value, column->buffer, sizeof _value); + memcpy(&_value, column->data, sizeof _value); value = [NSNumber numberWithShort:_value]; break; } case SYBINT4: //Whole numbers between -2,147,483,648 and 2,147,483,647 { int32_t _value; - memcpy(&_value, column->buffer, sizeof _value); + memcpy(&_value, column->data, sizeof _value); value = [NSNumber numberWithInt:_value]; break; } case SYBINT8: //Whole numbers between -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807 { long long _value; - memcpy(&_value, column->buffer, sizeof _value); + memcpy(&_value, column->data, sizeof _value); value = [NSNumber numberWithLongLong:_value]; break; } case SYBFLT8: //Floating precision number data from -1.79E+308 to 1.79E+308 { double _value; - memcpy(&_value, column->buffer, sizeof _value); + memcpy(&_value, column->data, sizeof _value); value = [NSNumber numberWithDouble:_value]; break; } case SYBREAL: //Floating precision number data from -3.40E+38 to 3.40E+38 { float _value; - memcpy(&_value, column->buffer, sizeof _value); + memcpy(&_value, column->data, sizeof _value); value = [NSNumber numberWithFloat:_value]; break; } case SYBMONEY4: //Monetary data from -214,748.3648 to 214,748.3647 { DBMONEY4 _value; - memcpy(&_value, column->buffer, sizeof _value); + memcpy(&_value, column->data, sizeof _value); NSNumber* number = @(_value.mny4); NSDecimalNumber* decimalNumber = [NSDecimalNumber decimalNumberWithString:[number description]]; value = [decimalNumber decimalNumberByDividingBy:[NSDecimalNumber decimalNumberWithString:@"10000"]]; @@ -449,7 +449,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBMONEY: //Monetary data from -922,337,203,685,477.5808 to 922,337,203,685,477.5807 { BYTE* _value = calloc(20, sizeof(BYTE)); //Max string length is 20 - dbconvert(_connection, SYBMONEY, column->buffer, sizeof(SYBMONEY), SYBCHAR, _value, -1); + dbconvert(_connection, SYBMONEY, column->data, sizeof(SYBMONEY), SYBCHAR, _value, -1); value = [NSDecimalNumber decimalNumberWithString:[NSString stringWithUTF8String:(char*)_value]]; free(_value); break; @@ -457,7 +457,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBDECIMAL: //Numbers from -10^38 +1 to 10^38 –1. case SYBNUMERIC: { - NSString* _value = [[NSString alloc] initWithUTF8String:(char*)column->buffer]; + NSString* _value = [[NSString alloc] initWithUTF8String:(char*)column->data]; value = [NSDecimalNumber decimalNumberWithString:_value]; break; } @@ -467,7 +467,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBTEXT: case SYBNTEXT: { - value = [NSString stringWithUTF8String:(char*)column->buffer]; + value = [NSString stringWithUTF8String:(char*)column->data]; break; } case SYBDATETIME: @@ -477,7 +477,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBBIGTIME: { DBDATETIME _value; - memcpy(&_value, column->buffer, sizeof _value); + memcpy(&_value, column->data, sizeof _value); NSTimeInterval daysSinceReferenceDate = (NSTimeInterval)_value.dtdays; //Days are counted from 1/1/1900 NSTimeInterval secondsSinceReferenceDate = daysSinceReferenceDate * 24 * 60 * 60; NSTimeInterval secondsSinceMidnight = _value.dttime / 3000; //Time is in increments of 3.33 milliseconds @@ -488,18 +488,18 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion case SYBBINARY: case SYBVARBINARY: { - value = [NSData dataWithBytes:column->buffer length:column->size]; + value = [NSData dataWithBytes:column->data length:column->size]; break; } case SYBIMAGE: { - NSData* data = [NSData dataWithBytes:column->buffer length:column->size]; + NSData* data = [NSData dataWithBytes:column->data length:column->size]; value = [UIImage imageWithData:data]; break; } case SYBUNIQUEIDENTIFIER: //https://en.wikipedia.org/wiki/Globally_unique_identifier#Binary_encoding { - value = [[NSUUID alloc] initWithUUIDBytes:column->buffer]; + value = [[NSUUID alloc] initWithUUIDBytes:column->data]; break; } } @@ -606,8 +606,8 @@ - (void)cleanupAfterExecution:(int)numColumns { struct COLUMN* column; for (column = _columns; column - _columns < numColumns; column++) { - free(column->buffer); - column->buffer = NULL; + free(column->data); + column->data = NULL; } free(_columns); _columns = NULL; From 4d438f19158e8abda42f7ca8e5b3f324257df966 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 06:40:43 -0400 Subject: [PATCH 053/127] Removed connection properties and made queues private --- SQLClient/SQLClient/SQLClient/SQLClient.h | 30 ----------------------- SQLClient/SQLClient/SQLClient/SQLClient.m | 17 +++---------- 2 files changed, 4 insertions(+), 43 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.h b/SQLClient/SQLClient/SQLClient/SQLClient.h index 01ea2a3..15d1fb9 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.h +++ b/SQLClient/SQLClient/SQLClient/SQLClient.h @@ -41,43 +41,13 @@ @property (nonatomic, assign) int timeout; /** - * The database server, i.e. server, server:port, or server\instance (be sure to escape the backslash) */ -@property (atomic, copy, readonly) NSString* host; - -/** - * The database username - */ -@property (atomic, copy, readonly) NSString* username; - -/** - * The database name to use - */ -@property (atomic, copy, readonly) NSString* database; /** * The delegate to receive error: and message: callbacks */ @property (nonatomic, weak) NSObject* delegate; -/** - * The queue for database operations. By default, uses a new queue called 'com.martinrybak.sqlclient' created upon singleon intialization. Can be overridden. - */ -@property (nonatomic, strong) NSOperationQueue* workerQueue; - -/** - * The queue for block callbacks. By default, uses the current queue upon singleton initialization. Can be overridden. - */ -@property (nonatomic, weak) NSOperationQueue* callbackQueue; - -/** - * The character set to use for converting the UCS-2 server results. Default is UTF-8. - Can be overridden to any charset supported by the iconv library. - To list all supported iconv character sets, open a Terminal window and enter: - $ iconv --list - */ -@property (nonatomic, copy) NSString* charset; - /** * Returns an initialized SQLClient instance as a singleton * diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 6916690..a89274f 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -33,9 +33,6 @@ @interface SQLClient () -@property (atomic, copy, readwrite) NSString* host; -@property (atomic, copy, readwrite) NSString* username; -@property (atomic, copy, readwrite) NSString* database; @property (atomic, assign, getter=isExecuting) BOOL executing; @end @@ -109,12 +106,6 @@ - (void)connect:(NSString*)host [self cleanupAfterConnection]; return; } - - //Save inputs - self.host = host; - self.username = username; - self.database = database; - /* Copy password into a global C string. This is because in connectionSuccess: and connectionFailure:, dbloginfree() will attempt to overwrite the password in the login struct with zeroes for security. @@ -139,13 +130,13 @@ - (void)connect:(NSString*)host } //Populate login struct - DBSETLUSER(_login, [self.username UTF8String]); + DBSETLUSER(_login, [username UTF8String]); DBSETLPWD(_login, _password); - DBSETLHOST(_login, [self.host UTF8String]); + DBSETLHOST(_login, [host UTF8String]); DBSETLCHARSET(_login, [self.charset UTF8String]); //Connect to database server - _connection = dbopen(_login, [self.host UTF8String]); + _connection = dbopen(_login, [host UTF8String]); if (!_connection) { [self connectionFailure:completion]; [self cleanupAfterConnection]; @@ -153,9 +144,9 @@ - (void)connect:(NSString*)host } //Switch to database, if provided - if (self.database) { RETCODE code = dbuse(_connection, [self.database UTF8String]); if (code == FAIL) { + if (database) { [self connectionFailure:completion]; [self cleanupAfterConnection]; return; From 21ee80806bcfb686ea9fe245552a119cd5c0de05 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 06:41:17 -0400 Subject: [PATCH 054/127] Added private properties for queues --- SQLClient/SQLClient/SQLClient/SQLClient.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index a89274f..13e4325 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -33,7 +33,9 @@ @interface SQLClient () -@property (atomic, assign, getter=isExecuting) BOOL executing; +@property (nonatomic, strong) NSOperationQueue* workerQueue; +@property (nonatomic, weak) NSOperationQueue* callbackQueue; +@property (atomic, assign, getter=isExecuting) BOOL executing; //Atomic because can be called from public API on main thread @end From d732af76b10d3315d25824aa8e660de089aa4475 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 06:41:34 -0400 Subject: [PATCH 055/127] Cleaned up header file --- SQLClient/SQLClient/SQLClient/SQLClient.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.h b/SQLClient/SQLClient/SQLClient/SQLClient.h index 15d1fb9..cd8bf8b 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.h +++ b/SQLClient/SQLClient/SQLClient/SQLClient.h @@ -36,15 +36,20 @@ @interface SQLClient : NSObject /** - * Connection timeout, in seconds. Default is 5. Override before calling connect: + * Connection timeout, in seconds. Default is 5. Set before calling connect. */ @property (nonatomic, assign) int timeout; /** + * The character set to use for converting the UCS-2 server results. Default is UTF-8. + * Set before calling connect. Can be set to any charset supported by the iconv library. + * To list all supported iconv character sets, open a Terminal window and enter: + $ iconv --list */ +@property (nonatomic, copy) NSString* charset; /** - * The delegate to receive error: and message: callbacks + * The delegate to receive error and message callbacks. */ @property (nonatomic, weak) NSObject* delegate; From 7181966c8b05a0d850528cb652ea7c4727eeda8e Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 06:42:20 -0400 Subject: [PATCH 056/127] Use global return code variable --- SQLClient/SQLClient/SQLClient/SQLClient.m | 34 +++++++++++------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 13e4325..bc6c8e3 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -45,6 +45,7 @@ @implementation SQLClient LOGINREC* _login; DBPROCESS* _connection; struct COLUMN* _columns; + RETCODE _returnCode; } #pragma mark - NSObject @@ -108,15 +109,11 @@ - (void)connect:(NSString*)host [self cleanupAfterConnection]; return; } + /* - Copy password into a global C string. This is because in connectionSuccess: and connectionFailure:, + Copy password into a global C string. This is because in cleanupAfterConnection, dbloginfree() will attempt to overwrite the password in the login struct with zeroes for security. - So it must be a string that stays alive until then. Passing in [password UTF8String] does not work because: - - "The returned C string is a pointer to a structure inside the string object, which may have a lifetime - shorter than the string object and will certainly not have a longer lifetime. Therefore, you should - copy the C string if it needs to be stored outside of the memory context in which you called this method." - https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/occ/instm/NSString/UTF8String + So it must be a string that stays alive until then. */ _password = strdup([password UTF8String]); @@ -146,9 +143,9 @@ - (void)connect:(NSString*)host } //Switch to database, if provided - RETCODE code = dbuse(_connection, [self.database UTF8String]); - if (code == FAIL) { if (database) { + _returnCode = dbuse(_connection, [database UTF8String]); + if (_returnCode == FAIL) { [self connectionFailure:completion]; [self cleanupAfterConnection]; return; @@ -193,14 +190,16 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion dbsettime(self.timeout); //Prepare SQL statement - if (dbcmd(_connection, [sql UTF8String]) == FAIL) { + _returnCode = dbcmd(_connection, [sql UTF8String]); + if (_returnCode == FAIL) { [self executionFailure:completion]; [self cleanupAfterExecution:0]; return; } //Execute SQL statement - if (dbsqlexec(_connection) == FAIL) { + _returnCode = dbsqlexec(_connection); + if (_returnCode == FAIL) { [self executionFailure:completion]; [self cleanupAfterExecution:0]; return; @@ -214,10 +213,9 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion //Loop through each table metadata //dbresults() returns SUCCEED, FAIL or, NO_MORE_RESULTS. - RETCODE returnCode; - while ((returnCode = dbresults(_connection)) != NO_MORE_RESULTS) + while ((_returnCode = dbresults(_connection)) != NO_MORE_RESULTS) { - if (returnCode == FAIL) { + if (_returnCode == FAIL) { [self executionFailure:completion]; [self cleanupAfterExecution:0]; return; @@ -348,16 +346,16 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion } //Bind column data - RETCODE returnCode = dbbind(_connection, c, varType, column->size, column->buffer); - if (returnCode == FAIL) { + _returnCode = dbbind(_connection, c, varType, column->size, column->data); + if (_returnCode == FAIL) { [self executionFailure:completion]; [self cleanupAfterExecution:numColumns]; return; } //Bind null value into column status - returnCode = dbnullbind(_connection, c, &column->status); - if (returnCode == FAIL) { + _returnCode = dbnullbind(_connection, c, &column->status); + if (_returnCode == FAIL) { [self executionFailure:completion]; [self cleanupAfterExecution:numColumns]; return; From 1f22ae259e6ba1527b766052293def9d0ca2091c Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 06:42:49 -0400 Subject: [PATCH 057/127] No need to cleanup when trying to connect when already connected --- SQLClient/SQLClient/SQLClient/SQLClient.m | 1 - 1 file changed, 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index bc6c8e3..481be38 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -106,7 +106,6 @@ - (void)connect:(NSString*)host if (self.isConnected) { [self message:SQLClientPendingConnectionError]; [self connectionFailure:completion]; - [self cleanupAfterConnection]; return; } From ab2040cee7d1829493084f0b976499f3de96d005 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 06:43:02 -0400 Subject: [PATCH 058/127] Moved timeout statement --- SQLClient/SQLClient/SQLClient/SQLClient.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 481be38..ee477bd 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -116,9 +116,6 @@ - (void)connect:(NSString*)host */ _password = strdup([password UTF8String]); - //Set login timeout - dbsetlogintime(self.timeout); - //Initialize login struct _login = dblogin(); if (_login == FAIL) { @@ -133,6 +130,9 @@ - (void)connect:(NSString*)host DBSETLHOST(_login, [host UTF8String]); DBSETLCHARSET(_login, [self.charset UTF8String]); + //Set login timeout + dbsetlogintime(self.timeout); + //Connect to database server _connection = dbopen(_login, [host UTF8String]); if (!_connection) { From ecb718a8d3f478fd0ba47a27a049d7b1fe2a3d79 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 06:43:39 -0400 Subject: [PATCH 059/127] Only call dbloginfree if login not null --- SQLClient/SQLClient/SQLClient/SQLClient.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index ee477bd..b4c229e 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -586,9 +586,11 @@ - (void)error:(NSString*)error code:(int)code severity:(int)severity - (void)cleanupAfterConnection { - dbloginfree(_login); + if (_login) { + dbloginfree(_login); + _login = NULL; + } free(_password); - _login = NULL; _password = NULL; } From ebb62508c3b410cc0496142e42bef92f993d4a25 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 07:04:02 -0400 Subject: [PATCH 060/127] Replaced delegate with NSNotificationCenter --- SQLClient/SQLClient/SQLClient/SQLClient.h | 31 ++++------------------- SQLClient/SQLClient/SQLClient/SQLClient.m | 27 +++++++++++--------- SQLClient/SQLClient/SQLViewController.m | 27 ++++++++++++-------- 3 files changed, 36 insertions(+), 49 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.h b/SQLClient/SQLClient/SQLClient/SQLClient.h index cd8bf8b..770483d 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.h +++ b/SQLClient/SQLClient/SQLClient/SQLClient.h @@ -8,27 +8,11 @@ #import -@protocol SQLClientDelegate - -/** - * Required delegate method to receive error notifications - * - * @param error Error text - * @param code FreeTDS error code - * @param severity FreeTDS error severity - */ -- (void)error:(NSString*)error code:(int)code severity:(int)severity; - -@optional - -/** - * Optional delegate method to receive message notifications - * - * @param message Message text - */ -- (void)message:(NSString*)message; - -@end +extern NSString* const SQLClientMessageNotification; +extern NSString* const SQLClientErrorNotification; +extern NSString* const SQLClientMessageKey; +extern NSString* const SQLClientCodeKey; +extern NSString* const SQLClientSeverityKey; /** * Native SQL Server client for iOS. An Objective-C wrapper around the open-source FreeTDS library. @@ -48,11 +32,6 @@ */ @property (nonatomic, copy) NSString* charset; -/** - * The delegate to receive error and message callbacks. - */ -@property (nonatomic, weak) NSObject* delegate; - /** * Returns an initialized SQLClient instance as a singleton * diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index b4c229e..06b6431 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -16,11 +16,15 @@ int const SQLClientDefaultTimeout = 5; NSString* const SQLClientDefaultCharset = @"UTF-8"; NSString* const SQLClientWorkerQueueName = @"com.martinrybak.sqlclient"; -NSString* const SQLClientDelegateError = @"Delegate must be set to an NSObject that implements the SQLClientDelegate protocol"; NSString* const SQLClientPendingConnectionError = @"Attempting to connect while a connection is active."; NSString* const SQLClientNoConnectionError = @"Attempting to execute while not connected."; NSString* const SQLClientPendingExecutionError = @"Attempting to execute while a command is in progress."; NSString* const SQLClientRowIgnoreMessage = @"Ignoring unknown row type"; +NSString* const SQLClientMessageNotification = @"SQLClientMessageNotification"; +NSString* const SQLClientErrorNotification = @"SQLClientErrorNotification"; +NSString* const SQLClientMessageKey = @"SQLClientMessageKey"; +NSString* const SQLClientCodeKey = @"SQLClientCodeKey"; +NSString* const SQLClientSeverityKey = @"SQLClientSeverityKey"; struct COLUMN { @@ -561,24 +565,23 @@ int err_handler(DBPROCESS* dbproc, int severity, int dberr, int oserr, char* dbe return INT_CANCEL; } -//Forwards a message to the delegate on the callback queue if it implements +//Posts a SQLClientMessageNotification notification - (void)message:(NSString*)message { - //Invoke delegate on calling queue - [self.callbackQueue addOperationWithBlock:^{ - if ([self.delegate respondsToSelector:@selector(message:)]) { - [self.delegate message:message]; - } + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SQLClientMessageNotification object:nil userInfo:@{ SQLClientMessageKey:message }]; }]; } -//Forwards an error message to the delegate on the callback queue. +//Posts a SQLClientErrorNotification notification - (void)error:(NSString*)error code:(int)code severity:(int)severity { - NSParameterAssert([self.delegate conformsToProtocol:@protocol(SQLClientDelegate)]); - //Invoke delegate on callback queue - [self.callbackQueue addOperationWithBlock:^{ - [self.delegate error:error code:code severity:severity]; + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SQLClientErrorNotification object:nil userInfo:@{ + SQLClientMessageKey:error, + SQLClientCodeKey:@(code), + SQLClientSeverityKey:@(severity) + }]; }]; } diff --git a/SQLClient/SQLClient/SQLViewController.m b/SQLClient/SQLClient/SQLViewController.m index 62b4363..4e00510 100644 --- a/SQLClient/SQLClient/SQLViewController.m +++ b/SQLClient/SQLClient/SQLViewController.m @@ -24,6 +24,9 @@ - (void)viewDidLoad { [super viewDidLoad]; [self.spinner setHidesWhenStopped:YES]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(error:) name:SQLClientErrorNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(message:) name:SQLClientMessageNotification object:nil]; + [self connect]; } @@ -32,8 +35,6 @@ - (void)viewDidLoad - (void)connect { SQLClient* client = [SQLClient sharedInstance]; - client.delegate = self; - [self.spinner startAnimating]; [client connect:@"server:port" username:@"user" password:@"pass" database:@"db" completion:^(BOOL success) { [self.spinner stopAnimating]; @@ -45,8 +46,7 @@ - (void)connect - (void)execute { - SQLClient* client = [SQLClient sharedInstance]; - + SQLClient* client = [SQLClient sharedInstance]; [self.spinner startAnimating]; [client execute:@"SELECT * FROM Users" completion:^(NSArray* results) { [self.spinner stopAnimating]; @@ -68,18 +68,23 @@ - (void)process:(NSArray*)results self.textView.text = output; } -#pragma mark - SQLClientDelegate +#pragma mark - SQLClientErrorNotification -//Required -- (void)error:(NSString*)error code:(int)code severity:(int)severity +- (void)error:(NSNotification*)notification { - NSLog(@"Error #%d: %@ (Severity %d)", code, error, severity); - [[[UIAlertView alloc] initWithTitle:@"Error" message:error delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] show]; + NSNumber* code = notification.userInfo[SQLClientCodeKey]; + NSString* message = notification.userInfo[SQLClientMessageKey]; + NSNumber* severity = notification.userInfo[SQLClientSeverityKey]; + + NSLog(@"Error #%@: %@ (Severity %@)", code, message, severity); + [[[UIAlertView alloc] initWithTitle:@"Error" message:message delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] show]; } -//Optional -- (void)message:(NSString*)message +#pragma mark - SQLClientMessageNotification + +- (void)message:(NSNotification*)notification { + NSString* message = notification.userInfo[SQLClientMessageKey]; NSLog(@"Message: %@", message); } From ef585cc8f25263b4450a325c04d1cb3daf21762d Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 07:05:39 -0400 Subject: [PATCH 061/127] Removed delegate from readme --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index 160e4b2..01010a7 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ Native Microsoft SQL Server client for iOS. An Objective-C wrapper around the op #import "SQLClient.h" SQLClient* client = [SQLClient sharedInstance]; -client.delegate = self; [client connect:@"server:port" username:@"user" password:@"pass" database:@"db" completion:^(BOOL success) { if (success) { [client execute:@"SELECT * FROM Users" completion:^(NSArray* results) { @@ -24,12 +23,6 @@ client.delegate = self; }]; } }]; - -//Required -- (void)error:(NSString*)error code:(int)code severity:(int)severity -{ - NSLog(@"Error #%d: %@ (Severity %d)", code, error, severity); -} ##Type Conversion From 256b051dface1599e78979515bb45c667e42a78c Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 07:08:40 -0400 Subject: [PATCH 062/127] Moved notificationcenter registration to init --- SQLClient/SQLClient/SQLViewController.h | 2 +- SQLClient/SQLClient/SQLViewController.m | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/SQLClient/SQLClient/SQLViewController.h b/SQLClient/SQLClient/SQLViewController.h index 9c3b079..0397bc4 100644 --- a/SQLClient/SQLClient/SQLViewController.h +++ b/SQLClient/SQLClient/SQLViewController.h @@ -9,6 +9,6 @@ #import #import "SQLClient.h" -@interface SQLViewController : UIViewController +@interface SQLViewController : UIViewController @end \ No newline at end of file diff --git a/SQLClient/SQLClient/SQLViewController.m b/SQLClient/SQLClient/SQLViewController.m index 4e00510..2555078 100644 --- a/SQLClient/SQLClient/SQLViewController.m +++ b/SQLClient/SQLClient/SQLViewController.m @@ -18,15 +18,28 @@ @interface SQLViewController () @implementation SQLViewController +#pragma mark - NSObject + +- (instancetype)initWithCoder:(NSCoder*)aDecoder +{ + if (self = [super initWithCoder:aDecoder]) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(error:) name:SQLClientErrorNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(message:) name:SQLClientMessageNotification object:nil]; + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + #pragma mark - UIViewController - (void)viewDidLoad { [super viewDidLoad]; [self.spinner setHidesWhenStopped:YES]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(error:) name:SQLClientErrorNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(message:) name:SQLClientMessageNotification object:nil]; - [self connect]; } From 4b76d50a88db56e5351a904f8acd43db0586b88c Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 09:03:56 -0400 Subject: [PATCH 063/127] Removed storyboard, create view controller programmatically --- SQLClient/SQLClient.xcodeproj/project.pbxproj | 12 ----- .../SQLClient/Base.lproj/Main.storyboard | 47 ------------------- SQLClient/SQLClient/SQLAppDelegate.h | 2 +- SQLClient/SQLClient/SQLAppDelegate.m | 34 ++------------ SQLClient/SQLClient/SQLClient-Info.plist | 2 - SQLClient/SQLClient/SQLViewController.m | 31 ++++++++++-- 6 files changed, 32 insertions(+), 96 deletions(-) delete mode 100644 SQLClient/SQLClient/Base.lproj/Main.storyboard diff --git a/SQLClient/SQLClient.xcodeproj/project.pbxproj b/SQLClient/SQLClient.xcodeproj/project.pbxproj index d0bbd48..36643dd 100644 --- a/SQLClient/SQLClient.xcodeproj/project.pbxproj +++ b/SQLClient/SQLClient.xcodeproj/project.pbxproj @@ -13,7 +13,6 @@ 5B0FC859180DCEB000DF4EFE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5B0FC857180DCEB000DF4EFE /* InfoPlist.strings */; }; 5B0FC85B180DCEB000DF4EFE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B0FC85A180DCEB000DF4EFE /* main.m */; }; 5B0FC85F180DCEB000DF4EFE /* SQLAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B0FC85E180DCEB000DF4EFE /* SQLAppDelegate.m */; }; - 5B0FC862180DCEB000DF4EFE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5B0FC860180DCEB000DF4EFE /* Main.storyboard */; }; 5B0FC865180DCEB000DF4EFE /* SQLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B0FC864180DCEB000DF4EFE /* SQLViewController.m */; }; 5B0FC867180DCEB000DF4EFE /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5B0FC866180DCEB000DF4EFE /* Images.xcassets */; }; 5B0FC86E180DCEB000DF4EFE /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B0FC86D180DCEB000DF4EFE /* XCTest.framework */; }; @@ -47,7 +46,6 @@ 5B0FC85C180DCEB000DF4EFE /* SQLClient-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SQLClient-Prefix.pch"; sourceTree = ""; }; 5B0FC85D180DCEB000DF4EFE /* SQLAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLAppDelegate.h; sourceTree = ""; }; 5B0FC85E180DCEB000DF4EFE /* SQLAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SQLAppDelegate.m; sourceTree = ""; }; - 5B0FC861180DCEB000DF4EFE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 5B0FC863180DCEB000DF4EFE /* SQLViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLViewController.h; sourceTree = ""; }; 5B0FC864180DCEB000DF4EFE /* SQLViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SQLViewController.m; sourceTree = ""; }; 5B0FC866180DCEB000DF4EFE /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; @@ -138,7 +136,6 @@ 5B0FC8C9180DDF7E00DF4EFE /* SQLClient */, 5B0FC85D180DCEB000DF4EFE /* SQLAppDelegate.h */, 5B0FC85E180DCEB000DF4EFE /* SQLAppDelegate.m */, - 5B0FC860180DCEB000DF4EFE /* Main.storyboard */, 5B0FC863180DCEB000DF4EFE /* SQLViewController.h */, 5B0FC864180DCEB000DF4EFE /* SQLViewController.m */, 5B0FC866180DCEB000DF4EFE /* Images.xcassets */, @@ -277,7 +274,6 @@ files = ( 5B0FC867180DCEB000DF4EFE /* Images.xcassets in Resources */, 5B0FC859180DCEB000DF4EFE /* InfoPlist.strings in Resources */, - 5B0FC862180DCEB000DF4EFE /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -346,14 +342,6 @@ name = InfoPlist.strings; sourceTree = ""; }; - 5B0FC860180DCEB000DF4EFE /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 5B0FC861180DCEB000DF4EFE /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; 5B0FC876180DCEB000DF4EFE /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( diff --git a/SQLClient/SQLClient/Base.lproj/Main.storyboard b/SQLClient/SQLClient/Base.lproj/Main.storyboard deleted file mode 100644 index 6198c47..0000000 --- a/SQLClient/SQLClient/Base.lproj/Main.storyboard +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SQLClient/SQLClient/SQLAppDelegate.h b/SQLClient/SQLClient/SQLAppDelegate.h index dc7a339..59497c0 100644 --- a/SQLClient/SQLClient/SQLAppDelegate.h +++ b/SQLClient/SQLClient/SQLAppDelegate.h @@ -10,6 +10,6 @@ @interface SQLAppDelegate : UIResponder -@property (strong, nonatomic) UIWindow *window; +@property (strong, nonatomic) UIWindow* window; @end diff --git a/SQLClient/SQLClient/SQLAppDelegate.m b/SQLClient/SQLClient/SQLAppDelegate.m index 3a4f65d..7b37040 100644 --- a/SQLClient/SQLClient/SQLAppDelegate.m +++ b/SQLClient/SQLClient/SQLAppDelegate.m @@ -7,40 +7,16 @@ // #import "SQLAppDelegate.h" +#import "SQLViewController.h" @implementation SQLAppDelegate -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { - // Override point for customization after application launch. + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + self.window.rootViewController = [[SQLViewController alloc] init]; + [self.window makeKeyAndVisible]; return YES; } - -- (void)applicationWillResignActive:(UIApplication *)application -{ - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. -} - -- (void)applicationDidEnterBackground:(UIApplication *)application -{ - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. -} - -- (void)applicationWillEnterForeground:(UIApplication *)application -{ - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. -} - -- (void)applicationDidBecomeActive:(UIApplication *)application -{ - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. -} - -- (void)applicationWillTerminate:(UIApplication *)application -{ - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. -} @end diff --git a/SQLClient/SQLClient/SQLClient-Info.plist b/SQLClient/SQLClient/SQLClient-Info.plist index 173ceab..cbea5ab 100644 --- a/SQLClient/SQLClient/SQLClient-Info.plist +++ b/SQLClient/SQLClient/SQLClient-Info.plist @@ -24,8 +24,6 @@ 28 LSRequiresIPhoneOS - UIMainStoryboardFile - Main UIRequiredDeviceCapabilities armv7 diff --git a/SQLClient/SQLClient/SQLViewController.m b/SQLClient/SQLClient/SQLViewController.m index 2555078..54eb401 100644 --- a/SQLClient/SQLClient/SQLViewController.m +++ b/SQLClient/SQLClient/SQLViewController.m @@ -11,8 +11,8 @@ @interface SQLViewController () -@property (weak, nonatomic) IBOutlet UITextView* textView; -@property (weak, nonatomic) IBOutlet UIActivityIndicatorView* spinner; +@property (strong, nonatomic) UITextView* textView; +@property (strong, nonatomic) UIActivityIndicatorView* spinner; @end @@ -20,9 +20,9 @@ @implementation SQLViewController #pragma mark - NSObject -- (instancetype)initWithCoder:(NSCoder*)aDecoder +- (instancetype)init { - if (self = [super initWithCoder:aDecoder]) { + if (self = [super init]) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(error:) name:SQLClientErrorNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(message:) name:SQLClientMessageNotification object:nil]; } @@ -36,10 +36,31 @@ - (void)dealloc #pragma mark - UIViewController +- (void)loadView +{ + self.view = [[UIView alloc] init]; + + //Load textView + UITextView* textView = [[UITextView alloc] init]; + textView.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:textView]; + [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[textView]|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(textView)]]; + [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[textView]|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(textView)]]; + self.textView = textView; + + //Load spinner + UIActivityIndicatorView* spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + spinner.hidesWhenStopped = YES; + spinner.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:spinner]; + [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[spinner]-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(spinner)]]; + [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[spinner]-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(spinner)]]; + self.spinner = spinner; +} + - (void)viewDidLoad { [super viewDidLoad]; - [self.spinner setHidesWhenStopped:YES]; [self connect]; } From c1100bc3e3937a812774c257e1d55a9a5c709651 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 09:07:23 -0400 Subject: [PATCH 064/127] Only show SQLViewController if not testing --- SQLClient/SQLClient/SQLAppDelegate.m | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLAppDelegate.m b/SQLClient/SQLClient/SQLAppDelegate.m index 7b37040..e6cc2b3 100644 --- a/SQLClient/SQLClient/SQLAppDelegate.m +++ b/SQLClient/SQLClient/SQLAppDelegate.m @@ -14,7 +14,14 @@ @implementation SQLAppDelegate - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - self.window.rootViewController = [[SQLViewController alloc] init]; + + //Only show SQLViewController if not testing + if (NSClassFromString(@"XCTest")) { + self.window.rootViewController = [[UIViewController alloc] init]; + } else { + self.window.rootViewController = [[SQLViewController alloc] init]; + } + [self.window makeKeyAndVisible]; return YES; } From 54bdf9143a80adc4fb6f6faadc00138bb29a4a84 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 10:09:30 -0400 Subject: [PATCH 065/127] Updated tests to use preprocessor macros --- SQLClient/SQLClientTests/SQLClientTests.m | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index d5c0741..b74435c 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -33,7 +33,7 @@ - (void)testTinyInt [self execute:@"SELECT TinyInt FROM Test" completion:^(NSArray* results) { XCTAssertEqualObjects(results[0][0][@"TinyInt"], [NSNull null]); XCTAssertEqualObjects(results[0][1][@"TinyInt"], @(0)); - XCTAssertEqualObjects(results[0][2][@"TinyInt"], @(255)); + XCTAssertEqualObjects(results[0][2][@"TinyInt"], @(UCHAR_MAX)); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; @@ -44,8 +44,8 @@ - (void)testSmallInt XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:@"SELECT SmallInt FROM Test" completion:^(NSArray* results) { XCTAssertEqualObjects(results[0][0][@"SmallInt"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"SmallInt"], @(-32768)); - XCTAssertEqualObjects(results[0][2][@"SmallInt"], @(32767)); + XCTAssertEqualObjects(results[0][1][@"SmallInt"], @(SHRT_MIN)); + XCTAssertEqualObjects(results[0][2][@"SmallInt"], @(SHRT_MAX)); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; @@ -56,8 +56,8 @@ - (void)testInt XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:@"SELECT Int FROM Test" completion:^(NSArray* results) { XCTAssertEqualObjects(results[0][0][@"Int"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"Int"], @(-2147483648)); - XCTAssertEqualObjects(results[0][2][@"Int"], @(2147483647)); + XCTAssertEqualObjects(results[0][1][@"Int"], @(INT_MIN)); + XCTAssertEqualObjects(results[0][2][@"Int"], @(INT_MAX)); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; @@ -68,8 +68,8 @@ - (void)testBigInt XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:@"SELECT BigInt FROM Test" completion:^(NSArray* results) { XCTAssertEqualObjects(results[0][0][@"BigInt"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"BigInt"], @(-9223372036854775807 - 1)); - XCTAssertEqualObjects(results[0][2][@"BigInt"], @(9223372036854775807)); + XCTAssertEqualObjects(results[0][1][@"BigInt"], @(LLONG_MIN)); + XCTAssertEqualObjects(results[0][2][@"BigInt"], @(LLONG_MAX)); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; From 0dffeeee02345a86ba83d8308ab682e0d7a60d9c Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 10:41:01 -0400 Subject: [PATCH 066/127] Added app icons and launch images --- .../AppIcon.appiconset/Contents.json | 39 ++++++++++++++ .../AppIcon.appiconset/Icon-40@2x.png | Bin 0 -> 4077 bytes .../AppIcon.appiconset/Icon-40@3x.png | Bin 0 -> 7714 bytes .../AppIcon.appiconset/Icon-60@2x.png | Bin 0 -> 7714 bytes .../AppIcon.appiconset/Icon-60@3x.png | Bin 0 -> 14834 bytes .../AppIcon.appiconset/Icon-Small.png | Bin 0 -> 979 bytes .../AppIcon.appiconset/Icon-Small@2x.png | Bin 0 -> 2831 bytes .../AppIcon.appiconset/Icon-Small@3x.png | Bin 0 -> 5475 bytes .../AppIcon.appiconset/Icon.png | Bin 0 -> 2910 bytes .../AppIcon.appiconset/Icon@2x.png | Bin 0 -> 8987 bytes .../SQLClient/Images.xcassets/Contents.json | 6 +++ .../LaunchImage.launchimage/Contents.json | 50 +++++++++++++++++- .../Default-568h@2x~iphone.png | Bin 0 -> 67782 bytes .../Default-667h@2x~iphone.png | Bin 0 -> 77255 bytes .../Default-736h@3x~iphone.png | Bin 0 -> 130053 bytes 15 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png create mode 100644 SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png create mode 100644 SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png create mode 100644 SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png create mode 100644 SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-Small.png create mode 100644 SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png create mode 100644 SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png create mode 100644 SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon.png create mode 100644 SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon@2x.png create mode 100644 SQLClient/SQLClient/Images.xcassets/Contents.json create mode 100644 SQLClient/SQLClient/Images.xcassets/LaunchImage.launchimage/Default-568h@2x~iphone.png create mode 100644 SQLClient/SQLClient/Images.xcassets/LaunchImage.launchimage/Default-667h@2x~iphone.png create mode 100644 SQLClient/SQLClient/Images.xcassets/LaunchImage.launchimage/Default-736h@3x~iphone.png diff --git a/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Contents.json b/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Contents.json index a396706..dff5bdd 100644 --- a/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,19 +1,58 @@ { "images" : [ { + "size" : "29x29", "idiom" : "iphone", + "filename" : "Icon-Small.png", + "scale" : "1x" + }, + { "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small@2x.png", "scale" : "2x" }, { + "size" : "29x29", "idiom" : "iphone", + "filename" : "Icon-Small@3x.png", + "scale" : "3x" + }, + { "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-40@2x.png", "scale" : "2x" }, { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-40@3x.png", + "scale" : "3x" + }, + { + "size" : "57x57", "idiom" : "iphone", + "filename" : "Icon.png", + "scale" : "1x" + }, + { + "size" : "57x57", + "idiom" : "iphone", + "filename" : "Icon@2x.png", + "scale" : "2x" + }, + { "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@2x.png", "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@3x.png", + "scale" : "3x" } ], "info" : { diff --git a/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png b/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5435c8c6145968e41842aabfacd029cea04bd538 GIT binary patch literal 4077 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 literal 0 HcmV?d00001 diff --git a/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png b/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..ee8d2ec7661d3458247bb8c86301a7c125164d65 GIT binary patch literal 7714 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 literal 0 HcmV?d00001 diff --git a/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png b/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ee8d2ec7661d3458247bb8c86301a7c125164d65 GIT binary patch literal 7714 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 literal 0 HcmV?d00001 diff --git a/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png b/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..20ffeb243fa49d430ab866164ab0039671b5db11 GIT binary patch literal 14834 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 Z|3B=kIwhG>qQn3I002ovPDHLkV1iufzj^=w literal 0 HcmV?d00001 diff --git a/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon.png b/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7f37f0d8742a435b21df95856bebd9dea849cf4f GIT binary patch literal 2910 zcmV-k3!(IhP)Ol}CAhi_QstpMxK}}MTgd~u|M-rcFdw0F3cV}l_|1en`Yt}n!J4q|`NvoMR zzxRIc`n})#{(ir=l3^HvB1w`Ygb+o)S$QufjhA;Rx-3Z&k|Y%hjLeT@V)h(OH+whZ zZT7tsuTnI#2|N8%N>wVw%<2kF^t+XB#qx5g4vt6cCd$LC{CN6tx{h4VDvWo)lOYWg zX;=PV=$yVjNSiaaC2_Z-%Q}jn-B$BCd?`R$Sk?(1`NecM^`VQnktY$)AH0-2haUEI$1|Z*Ve5Vi3FY*GYD^a9h)qZ z>F>i+U5$6n9Ml;z2&*0%mn}u{`OfViC)1wn*}4@;2#moPp>^LHRyy6?^uO>tq*5qV zRpeWaVybH?h{tfBYy-!pXd2>Jeg@&oYw#__Lz z09!>R;f+sG^WeiE%LP8UaG0(wTR8dJW@=ZjX2$yOVGjf>tcWfuy(~BGo5?paD zir>%9n^)8P#0F}vxwoYAn=WK;8elsL}b#4Od9feY;_@!B;1!R1z8@htLq$l@Ie~y zzaN{&^W`2>6fWKPBeEm0u2@Ox&>yKX)6}9(dC(Q~e3+Cl=bo}xcID$bG zmx>;ZlKT7K@Ls-@!0NT+r%VGOaJf`+xg0i|4Z|?dbsd+>b$;wgGe4_YUI+n(!Ldgl zCD+!5O%iCi9KKbzAYFM4jkB5nh$oWR6$O_|1whwzGMOPH2|OOpg#E5&il?NwmfF-K zxO3!@N9cR|ZK|%hnudGtr88JZZ9|AeGR0s#Nx<(Vo6QvlXcz{$oQC`b04NpyD<@QE zR(3)F-Ow}+J@^pewiC>KZ8HtauON|1lRMFg+oj@mskEQ!!r@TJW^>qNnRp_J1b94d zG%Zg)pD)W$wgS-0QaocharIP+mX}|l;r82zNh+>6!(w@~iei*gv-cM#IOLuQSx*@0x_|SEO zfoP0qEKW|-sPOxcWC>aR(m^=viHX?Z$(`UqC|u`4#F)=w>j1qLE9JZ@LvZa~2A9fp3XG^un3kJn>aG_8aQ z%;-v~0wXu`o&%pTeM*Rq?l9fq0rL4gt}kF%b+Ce=Y?e@MH9oJqz~e+`56yFD42!2> z5RWI3Bnhv_&EQ}Hn=D%f0al==X?(KOrXBhH^W!IIp3_8y-^Y?g^N?kkp=^$wdk(N- z`K9#rNBL;?K9*dxfa+j??%safE|qLnqq}zir^8N7RV9jIBQummu`6YH#;s!72`L_3 zH*mXDYN{#<4edSfIm<3xh)tFW)mCw(`yxI$c!a9T0DS{dT&feV$IaCGx-*naGL^=q zI?K|M`tYyZ0R?o6`U|(sGhnjZ^TrRU}Cwo73p&8$bYUr@9b8 zWx!8WWdKF7oxwYvNaAt3OWyS<=Uz}zdO21|HmC82cemkm*jc%JG5ygPGp9GueyW@P z$RLW%Mnk9?RdsUU@Nura;u11LS^6U}3`3wg?YLboY&My`fhdhr>&y0usB$6roK940Q@mToTit;Jx$SQ?hb=}}t+bKS8Ie~=4%*F-= zqJvDI5<=64F==JMM^$B@fOJyn49QfQx|*u-#WluZG*6mmAe+<$wih%%JT;jr8B zxK%0wJ_0^3UXKgKX5&;>FE(_dkq91-hsLI+vYce+b&zK7nwBS(&XCFG&@>IjX2a`o zBVmw9r^#lsOr19E><@A>jOMWvhLr$~RmC%2?0WW10M7rNyh*#qV+8|S*%D_1pv&ib z70<=3xBuU*Gm8=xqnGQlD7UUzbgdNq^i#2l8cO9$?F*|`g_Sy1#C+sHWh;BA9Cu(T zLwBry50s;c&HnXJjE`)kfyuagK5})(IyZ}57A}Tett`NQ0H8-@no?68@&Et;07*qo IM6N<$g1?!bTmS$7 literal 0 HcmV?d00001 diff --git a/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon@2x.png b/SQLClient/SQLClient/Images.xcassets/AppIcon.appiconset/Icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ba58d7a63e91786c922dfcd012e71c8de653de9c GIT binary patch literal 8987 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 B;U@q9 literal 0 HcmV?d00001 diff --git a/SQLClient/SQLClient/Images.xcassets/Contents.json b/SQLClient/SQLClient/Images.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/SQLClient/SQLClient/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SQLClient/SQLClient/Images.xcassets/LaunchImage.launchimage/Contents.json b/SQLClient/SQLClient/Images.xcassets/LaunchImage.launchimage/Contents.json index c79ebd3..1274433 100644 --- a/SQLClient/SQLClient/Images.xcassets/LaunchImage.launchimage/Contents.json +++ b/SQLClient/SQLClient/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -1,19 +1,65 @@ { "images" : [ { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "736h", + "filename" : "Default-736h@3x~iphone.png", + "minimum-system-version" : "8.0", "orientation" : "portrait", + "scale" : "3x" + }, + { + "orientation" : "landscape", "idiom" : "iphone", "extent" : "full-screen", - "minimum-system-version" : "7.0", + "minimum-system-version" : "8.0", + "subtype" : "736h", + "scale" : "3x" + }, + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "667h", + "filename" : "Default-667h@2x~iphone.png", + "minimum-system-version" : "8.0", + "orientation" : "portrait", "scale" : "2x" }, { "orientation" : "portrait", "idiom" : "iphone", - "subtype" : "retina4", "extent" : "full-screen", "minimum-system-version" : "7.0", "scale" : "2x" + }, + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "retina4", + "filename" : "Default-568h@2x~iphone.png", + "minimum-system-version" : "7.0", + "orientation" : "portrait", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "subtype" : "retina4", + "scale" : "2x" } ], "info" : { diff --git a/SQLClient/SQLClient/Images.xcassets/LaunchImage.launchimage/Default-568h@2x~iphone.png b/SQLClient/SQLClient/Images.xcassets/LaunchImage.launchimage/Default-568h@2x~iphone.png new file mode 100644 index 0000000000000000000000000000000000000000..d23f51f2ed254be505c83ca40b20bffae11bd3c5 GIT binary patch literal 67782 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

j01gD4N< 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 WTk~=XopCj6Cy%KgO+S3`=Dz`rs;j90 literal 0 HcmV?d00001 diff --git a/SQLClient/SQLClient/Images.xcassets/LaunchImage.launchimage/Default-667h@2x~iphone.png b/SQLClient/SQLClient/Images.xcassets/LaunchImage.launchimage/Default-667h@2x~iphone.png new file mode 100644 index 0000000000000000000000000000000000000000..8f46fa650f0562fcb8d33b1a3006f943d5abc246 GIT binary patch literal 77255 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�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

QT2vU#e1F4cNfD7#SNEg!k|ysb z{QNxWMWgE_k$j*yoAxpb?AxO5{+|8w%eRs#>fnvx`r~HHekL(TgT(oHC*Sj4VJU9C z%#ytHyq4T${7QQKJVkl%XbPr1TY@U9o#*d`Gg(!(7${)l$jHJ{|-5{Gldw zZre@vi;96SotUMJfd*ARNAOP`Wq*H3ME}h0avBvv`2F9P(qWqU%8IIeA};qdmh|C@ z7d@Z+pNtp)2ASa_H9(Tmx^KT%<8tOI)O~V!1)wc)&lPpZTf8d!DNj-M&(n@;6*l5b z{p*LJje9CMAfsDaU7Skyo$q@$8bb_DdatjR^YqkRblPQQb5*|*Me|X~dWka~?T^jx z%Cp6$@LO#)E`RMwfEwy;T@JJal|XdA_}LUSa~-e!4ZL%ol(EKTqJq}QTLIcL#FEvr za}cL?b-J7usCLE`q|y{M+9 zrYCkl=gt=?^`%JQcEI`^!F1ee1B@Qj%7eP(Q@8wMSj44L?S4;#|gS1KKrV6|UQkDG-Zrs(LF$O%Ily^l5q^5X!Icj`;MxClg& z>8Oj5UYCGVziH{%tSiLx)S}U-KRTqXz0+rU>-X>9J(Af#U|tFZHFwUkuJ~yJsj(m0f=E8 zFq0ZFp5n1JRhsptNa&q|+Vw(L+_kWWqm7Nt&QGoFKaFv*aqXt1)0RC_0at%BUJE5h zU5+}utD?geG+oSGk8$7+XGv0otAnZVL&V{*+UY=3DFFQRFPF1HFD26qKxU*UxvbLh zO>ubU!;V#_Z{rcb&$O(@u7PPrMNDi|En&zHO-eg!G>sB#zvS^$t;=%L`O)Oxt&c)j z9WA8?4{iZOCL9b<}W4- z+2a#gPQ7s7xFi)HsC-xbmuua<{<5Uer}^x{rS1r;Pm#2N8pe`ZS}L-}-c(VC?s(wY zH3RVfcm}v~^qc&lGIV6WUy|V`FYxa6nF4$_RD|!AEbpEA{vTK09nWR^hTZR}G$kdn zlQNRMvI|Wsl0Bl3oxOQVRzih@5JIxaUPTC5*;{5d+50`d&-?!M`aGX!#n*jb_jR4; zaUREUo;RMZ>sq_2X-7r|UD)PGP?Pw^=wOq$OSdOha3m-qRBO@7Dx+*;{L^#>i7ds! zmaCVSm%qpQ)8WPRnUPPO(=qP^IbS^-9vL3r9o&<9&v%>J_W6#xhr9c3kUu%QW>H83 z%T1&KgKy7*BzE|?^7g(5D+~xafCC52+;f^Q%{A@Fd_9)tFfb|j;NQ*>y_?z>lvo)q zunIY~NAS;RQO378HvJ4|ihq6bO?33`hQ}V;$-|!~>YDi?pC4vO>;00~? zU9RnOqVmSeu40Gz6r|CSx=^y~n5lNBRNU+`(LR8d!)FEEw>x#+Lc_vpxPB%EP)~?2 zTy>qzZ9D6&zC98&^Xlb|_(H7SkGY>kxuzaECGU3D;T%wq*qV92HDCTWB20^;{C73A z4>7$3Pq@kStXYnl3cD@`+0nhFmYd(CZtnM-igFZ-7SwgRp3#|;PYC}$F|O7IHxE9& z+MHn8xp!Hst4PX6L1On^f9ZVLhT-y`7B>#_gPF!7)n8AxwKhq3R0aqV?Xl^ed)%5g zE0a;8pQV=G_j_%?iJ6l#!&&VUewcz|rmb+vpm$+odv&(Ad~etI@~xSuh&f8$MYo5+S5aZvQb4&Bh`^ivT7?!O~87^Gj$YLMH(uCUZa%!UMu(( zfVs!u7uN699x=kXNr_|w1y&QaXZCTk= z^ZmiK%35063nfkr98c3U^DXvbrQn6c_}ur~NP|9e$5;M53<{tX40Mx@8QjCWdk*fg z$+d3qt4pV;tj+f3=WQt_D~HXle%pVjBTfDEdgt2iQVg4oY$ofyBqF90yw5M|d|}}V zyQubr%`elg_8GG%Hy^D&-ni`icraS5H_yVnH*dCot*??Oad$tFD33>3n4UdK$LO6c zY>0ik1^ZwrihDHt-LL1}+0}_NY${ix8hi4za!t-`y=Eo%XTI)y%Wx@C4%i|`Vr{B_ z>!}Z&$q+3QGjmhHbb8Nj`}~@vTCu{5jaquz0K(guVYU= z^dAdnF4rQZXh+e_bn3e6S=Qfe`zbQ_r#6D1y0sWn^rAx6uRz|Qh1A;FHcMGn+RdOZO7G#nbxFy z%WmuTh8HhhbYy7P?}{!LO*^m6(SCEoNiN^nb$O7g-{`j1t|I7FtQdc@rcL?_+t|be zy^!_u9U=aU23ifIhuOu%+?tzXXPOLiHUO5NdJG>sP!!=~bA@K+7Z-=@jfdmAJ2Op3 z!)F96Xa((r`-%CtWLs^=bv>>xy`St$s82dZ;aoc4FyK6$K%wIZa6nPAKI~#yTE+Df zXX6*Q7_Em22<(3gc_`!}#oemvLT7yIrwr<=1F4w{;>aid8q{Alwf}AL`|9eH+X{%aPP=1aRs0PvAA5I~I4$9IF@3x?_I}%HEsKP&C_{pw%%RRa%&{$>v6ocq<%~z&x}$J1UN9L<*-27NBMuk z(z{Zd7eq#vHY#4p>AdHs-xj5tuhVy6IP6t1rNn)Fq)bnO=@oNmSxXfa*>T0P47A?}@+PKU zZOt}Vig7&8&(FWU29P``;j&r-UO6tCK6xbiKwc8di{alPL`dv~c>#31iEcH4Gd@e7 z@2C93|0L|Pdc`hk_#|m(VgVTq-U-i>73A;D&HhF#k_Z%8mB+SD9_QmaI(PP@Y)ZSh zsO7*5U3aJ7V__mhaw997t}$+ZYdy->&pST6-hAt27Dp3iyAXMM;qB#@|D65jKgf0~ z_O5I=j~A^q3&*9NB7{y7lE!BuouO<6ff@6DzkF>GfI z1H^I}ormgbyf{vsrKgubI0;MB*_k(rX(wVowm0n0Y3(U|hl!|tjP3H}(K2QO1B1W_ z|9eLk7vo-0wC%(G8jpA~{qc4df50&W;gPj1!Bou}`HG>X4F_vBCnXZ|o-caLgx0W? zwZZWZF zj#rZMG!k=1yGcaVR8&;XDceo{I%vd6m|pnm`Sa(j`F$04$rwg|{I2wv?RNot7S+q` z`rTLDZIiEKBr!3u^ZT>xU%$-XPuI6=uFds#zdrRskB67n#@f1|uWrnGHr3b_4N+U| z15rafrCZBK(#JfQ1O*ptQdH7<9R7UWbrleF*ei3>ntPQv83pR@)&tM;ihj)he$yo^ zxNJTDMx#i#It54AbZP#8u*>RY2H-=5Xr6@MrXNiiL+{)SpgObOWLdsjlodvff>2A) z?CjP{EsoC4y&cXl%{1NeUD<}**r%RExu5IDP%0N6x*7Mi$-2;W^vCOV%4d3I8=Iya z!5>Hr42dKaCK{t$_zTvp7sw1TOQh17g>b6KKf5_^1_<_j-F=U{cCllPx0z^(zbQC1 zGGMheyU5RLXx;SQ39u_OCQ(oGg7;O$wx7lG)>XmD!m6YhBC1Plq^bV_+>Q~(h zlF&KZmcjk5N`GU)lSCy$qy5#*ncj@SFO#j^Er-ho_vRuk-B%}*a_w409f!Vpt_|uc zD@z~NJ8M||Gh8a8n8R^@SpCWmDUvj;tl`i9Ljx$gZrYZTj~r+ zly46Ecj`D*J?7G~Q?RzSrh6~4W~)N3>FU(B(y0(3TKKz0mnrz5A-AfsvRP@|8C9}z z{r&r{MtwMRFKu&qR^&;QzrAy9p zlSv-xxl`3y4~y;Q>FDV>PaZWF==CZoNs!q2Qk*ERGx72zx8Q(dcY{%5gdMifpn%1r z)0F_=v;C!8d93%GSLSH(W2gLSPux`0`<)&oBABjg`RTS$nnu3PLXT+$8G~|B=O|v_>|kzIp8RjtgPcMeDr{2`BwT}7Ok!6j2X+uD3P_@;`tZS=L;nn&|asL zQKf%`Bcn6TWzAvGV=r6MmDmcgOM+9qVlJ)9%zbS(f6Th@?gb3{n(fJt5i>fm%eQY| zM5yW9KfxlO4!z>+yBthyS(0_{2GMojTbIjb14Se)XO=epQKdK0lmXiLaQKezO7$tf z(b=^Dm&s7o;o;$b%3&;H3cj7uNli>-KX*dPS0ioiZq7mylMGjSM7@-Kv6!-A|2Zow zt7GNs!+UgY$_Yvb-63WjCz3+2eG-Tf=s`at*9J4nMabH;L%Z1P`my<)2?*VBivNS0lXgZ9E^Ug%n`$FGoJ5H zZg;ehO7zKlKb^~m;PN@ZzpgaY&OzW5W$>Y)Gr>LoWQO@>SlJhr(q z@1Y)&oSNG9ICm0AW~SYA^pheDb0x~@Z&Kbb>3DA>E5VFwU%Wr)mLduWtxk)6ix{3U zuP|LYkEMp(Tzd=>sq`jpt*Y8^6A$X8E%}E{Kp@X3L&s*We6O2A_qg`MR}d_L*CD~Z z@b^bU^vL3Y!Jr!{4L^RodorEIS+@ME&A7GV?HWI2fMhHeLMr4cD65p3GUme%P-V@x z#QW4f%2rRCxpvaO3Mr0&Ft2$Y8qwar?}K_QS3uSVI3efKGFUhabCsU6gxt|KBYa64 z`e=pr&ZeiP@+sfP%h6t(EL2Kvoy0yVkcexVXq{fcqq4{$l?<5?`(C3f5FAmzque3t z5TQ4E^t!ApfofgXzsP~Bmb(!@I3IQ=0UL)ZjogSp+8jqZzWV(Ew*dYLiU0rm`+tA@ z|GsJD+Q>6uNKj|aoH5Mle#E1mrV`lz{-&$UPn1sK!l~LZ>s*aArQ|jfJ1wogP_GTm z0~qMX6OOqun7Q3ME1%RlYY*y~VC-GGc*;*Q_Eqg8lU$AXmd7T}>QcuDYCe#OEWQs1 zs^20no%_1tKTrQP*MUUNM-}ktZILgbL}>`?oMz|X5O-dN2z)a;JG(JP0>kjD+^bzo zjh*2Q#fRW39`k{(fT%VL5+y^Yv?tfJCZW+R_(0g-zki{G&dkj8_4VZ#HK`~otEH*< zRQ`saL4G3i;MVo?q|}eW;(`_i{UmHR)rM#H^5x6t&ky7sy4NafGcJqu^A(F_`D{5@ zW?i^VC7QxcE~BW}f*hKYlLMsicWDW?+-_o5+;3)RU@E6bR)w)Joe^^BtgNgvru_*r zVW_@qs;b&^hZy!f}rp8T#DD0Oj|?Ey8P)taPmXw5k_|NHmvI;GCUlsu0>3hXC3{odFIm{X@^eG{Ag^1^_RF%P#}@yEZ0!cbUO8wCm%NJs^bW& zELSmUySv(JIq0_5A4n@Q!|z7VC=+{kd4ABN@x2g!@5Ex9m}>He4-cTwnRcXhL(I94 zR}zRJa8CB*B0>f;DMa_>ns)vv4_xcBX$q3qJ*&T;F315_IR1(?eh4^I=Iv$mVtaEP z9i8vW>NE3o1<{CSq|@yP+HeIJf+VFFPlj4lTCZ;5HHa0&aAqUINfWH)IF zOq@iJXh1r;_-N~=Z>vde{-FC7y~p;qJmR1Tm$^db<-o;_?U9KM6C(4lXZEyI8;^h$Xm|<;9D}hvtK2ZoezMs0A%j&D|9c z4FghCv5A~l))z-&T>sWiXOz=1Yue%kfer&s6L3tBSX>(T6esch@V>h{yPi@DtqKzT zPb!I9esU`YhlcWFfviTV)6{dAHiekF5`n7&s0CeCXSTOp-&+TmgIN^pdr}FSh(4Fb z6BanE|2W>S7{hO>Vr8``G2>LIl@3A4rYlp=pMrDMQM5rX45TuknD+_V&9N|Dox)2; z&l|MFUB~@OSdSh)aS*3@dr53-CWo_p+d9}a_3y@Zb@5kQWO#5kW$VNKcpq3H?b4e! zZ}zsz_fR}L8ymCkZl9kERuB*8H43cK_uG}aIs1&2fLe2b5JvhP26raJGUnlysHV2W zqgAyTs0G3Cl3qfbm-n>CQ_^KzvjprWa{R;XpC)PcoqKL-+cU2Kr19X}vrQV#r~k4~ zrl_QxIqG%w+`@({0)u{5roosNhw42s)`&!1$KPHKgKqgxkIg_wkc@49%$Jpwbt&}D zg;PhoB%xo~7pJM1-oJlF^H`TfzToInY)koch{S#v>qn6P#A8Tm$O%OqG^M^Gcc=Epd{RCqXa&sFg6PD0z8D9GgnYcq z(v=a5O}FD-+@Ib#U0+v+h>@L>qwxNU6=cGMeJu_JuzyEPOI^xI__OOS2T^S z6fI=Kv`a=E_wnOstPkC1)0cCIR(rcUI9a{v+Qq0^-n~2PCtWeIR%b_6Xs|=;R1he~3=TM1dQE-(2b1fN^VLu6gh3XH&i|75!_U;L_u zGX-Ir_51G&zdM}I_!Ps(?Cifd7{IPr!d4*G>4>l6Kf^1zeqzf6lvg{ujE5TmSJhZO zOj8jt{`HZP*Km2ZS2d;Gy6{LP*NynaneH5ywYeR_L829Yo-Dtw`K6Yrm#zEmhEd?n z8|Pn(?rkq6BqR{KO6T`Rg@TTjWhT`T4%?sEA#2r|H`-KiWQ=S+rQovg+farvCU;QEbS#p zS}*}I5iX06sek_cQ=Av~vfpC9H=i@iVY?$u)3Q7JVs;QR8abzqsP*VA$y<2j=g%0> zxh=O8jlW~Pmk2vX!D8SNo*vd%BS{-6*MEpZTST#X)}Dk~;AK52#%3a-YCodiA;fmk zC0Pv(Duzev3%||S?hxSdjW1^1JRFvrw8FL^?bR4(JwbOA*-mb6EOi^_5HEi_MBm2B z{l&c39yiA)C%77Ge}x7?&q`MI_?p`^qOg;bP@HU3s%Cy*eGvm3L{3ZNH+p;k7A9gm z+^b(wKGq_H_W=JyV-Bw+P;S^}@tG3t<>gP;7O;9Y;r*wcY|Z9Vkda9*y^FbG^=#zb z|E2>)<{q!>6(+|01W8*#cRyS<5Am#e;B-zm&6vFAzI~~tpVEJR^E`x#nc6<*;4yOP zq~u5YkCjrKIU}~*EY)XpT4M40X_+t%gN%dHa`>*!G)+O%j?00)8Cr$e1OA!m*Fg0X zHbAAtYTG@90D6hCEQcEjN|^sx!m{tATj$-74Mdkp_XQG~D+_SBneH}uB~CI&JD%s{ z40>qjJYTvdzCN@+_U_poZ5(Ulf?4oPS1k5^$#7cfzO)GdPj$cRU>AY!9`<8?l1iYt zs=!1>3R<2h?v4)GF^}!VL%Ua`>Kht}UGC(Yv`-=D)@1!dsFa#aat#d)f$f?j z`yEFD*oU{KH7!d+V`7>N>OFR*brJTQdArSH@2*!-=q^vSv(3EalPh1Wrf&Ec>yVS{ zvA3H^A!}Lin<()5!hcLw?$SgPy|69IjG-O7*RKmF=*73o_jZEn-%5EO-&k%{puSRg zB^x`XZ2PZFf)b^DJ=V&yEAxLIF$^>!_LpY_%t!$lab=v$%o8|hCrSg9FU__k-&^wF zz<-v3j4BS-NZXxC$tZa4%|gxhxcQ!fSd~G&*D;E?FugxH!plVZSp_x|jaTlXT3AxJ ztE^0xH>o5mD+{p2>v4W-ZGI0AxzKC_s{pCJ)Jfc*ImR^`|Eee0OQ8Hy?{kEu zq^4e87C3U`NWI)esdHvW6#WCkQ)1eA^vHm)Ly0`u_<|7`={)Ds|u4%#2J{{+vJH zoT4uGy6_ja{*9MsN$qo1raNuk54omcZ)(Y_a)=S{8;W_V_s(>+@L0MRGi&IbN);cx zTMTc#L=Dj`?hm-vW0sd0*$^X94*1freFm*oLE@W_yQXhK6z?yx>#LvE7I`kI*M;$3 z8tml$D03&o-2AKJI;W2vJGS)qFNo{oM4pwu7o1DcRGFXz?EUH$jGjK&YgIKhf_p6; z5B>wrSph=(%Rd@ZJv38I+yFS<-rnXlYDD@82@RcGaZYKk3)HM1`}^+QyN@3~B2hv1 zw4G`LEWLg1F=dPcX5d0+owX0EuM)66B02_jxUH?NJ`dBDs5|gh(pn_Z=XxKNjAPt) zfMJ+JYfl>NDxNxZs-i4b_5OX#A5Th35^PF(kkjqHF&35?iOiEn7mywKy+LrW4?sHp zW{~%Q8E$!R&10`qX}~iMhrD*|Z*y)Db|TRm z;_HeG-hF4ijq~TPXC^1A*!Eh;#>5{)d}MT~&eIRi8t!baoMx6ci&prJY^6t` zM=BZZIP5nrnn@;m{`Jpy+>B-+LaysifQ)MbX-RcCs6%hd$cQ-oee@?I4FN0to=DTU zpWZtFif!aVY5}vM^_D8UzZ|&J1!32Pf-~g9evK>3TSa=-D#{Y6suEs>%wRAhZ&tBuImebIzu!7 zD4;(b{O6iEeJ`AdBS8}BgD(#FS7qZ~>D|}$5ZAR-tcX%Red;Q*LzSU9=scYUfa4CJ za+*ojcZlL6jID}8ggv6Nsd}@ zx5RM~Zf_kWrEH^4j-bwz7LytvWf4UC-bA@3h^xhTpK;EPJKaXUpGOIh8jdU53;{YO zDa2SrD?F8{8i0>Ar@QZ&P8f_t90&WLNK@NZm;K^x5 zw`(g&BxvlP^FM$&`Drh?Rsk0XA`Y#BuUtRn1pBz$XS!5%+?~dkC#5#m2Di87k5_KP z3_Ig8x9BL^S&_8OPipWu0tKO97-=UyQ=oDuzuQc2koyS3eA$+Tzl(8!ty$T0dhtnv z^e-0u!W{E%oTbZ3$K0NUhldBW-MYZdy$HbXcPoTa)bYum8WWs3C#MYqK~a>~|Nd|P z)R(4jiq-RLgSM@5dX|4@^9OG%tq8lWXR2L3Q7VI=@2)*>58`D1p%Z2wE&rmP=3rwp zUALjX-(^Qd$`VoFNgynNjWj{dUG6Q68ZFpFe-z=pm#$JdZ5Y zZGOAPna(Ryey5p%-4opf*EU?$ySo`p>YR9=A4s?U#Yam)QL`)hXkn=Q1brF#07*uu z{hK=pe^I53WRC%tRLBqcW}_$-q6uv7U7qgDxcJtdP=<7UYmbYIo0&0t6vpO5jtEO( z&+A{s15XMHwYPsu8l%Nr>e=&4mVe0vMCq!gY1mZ~3({DO3OYw5dMkC0jBISjy2tM9ZFZIy$Z8Yn zkM}}0KMie`JZ@m00BOu1bj2+_0s9gm>SQfNbgSZMBlkp8451vB*6~HwLQD}~76=Uu z#W(vlz9#TEC_ALwel0lS01r7@BIK2>bZ8dxHTo-`D4vLR0cP*(ihaAoAR>b2*R59` z<$Z%xGRH>RM4mTPW7byK0Xk>=(nP*#XW&lh;5><@?*YX`uSo>$E>^Ns|p zgKBJ01#%_vU(ap!x@28gg50AgIPyz)k&5rp54>r;G_?LLA-jmJ^*zi|i&$z^2X6p6 z1T+bN{7G9itMl=o6VQ zm$0aKevq^5EX%>@nBd@ux86WoOW#i`Jh_}hC}I~!YUo8BGi%p2z>ewN^aD0-w+>@l zn`~vC5fBm*3J`1TlN3YIMZ=Z^W@Hs`%rZ|k0u`VgN?yT7KS~D>qn@Dp;@@t)_=OXE z0@xS-A=<#R+~;RjBE(!)rDJvMuU-2F_p78OuXzt2NA6MV%w9N}I_%QyqzkmtQG;b< zmGisd4v|=1d}S6yd{@m_;kVh_;cL)&2D@OY$xS z;ew|CdtDd49V#?)#S_KCufPDC(yE;6D+23uy9sfr9JUY!(I0bDL^QjmsQhQc58KE= zIEp>R%+wYo=o;#6Lgi3kR)TyLX<4@6L+?J6eU2kSz;eJ9f6%eAKNWvMNl6J07?LJP zi81T@%F4L;-v|fk(+-;tYXZek+kp3Y_+7-E>p0`33dK=Szh}2VTu5`|5$di zq^gd1puc}H!qVQ>;GTCuB`_W8?m;x(1Z@7r(pbiNez=oRG!iIMG0;TdG=4A!Ju&8` zI2T-tSkwOhNW+>Mf9do2y|7e*({llQL6Ga195_x?iAR}_ZmF~U*#n}~ z;>C*>E*$of^siD(IyT(D+_k#Nh-zw`AX{p%tl<4A-f+oN=Dos4|>7U^>*FRNw#o5nbB0hN(wrpN|rqP#v;z(_uEaq;ET z%E`*m8!Po^Qh2BW>@>O~LqfjTPW~Ft*+XPexN~Q?Y!9K=Z6>P*2Rsp;(CtbzN?pHx zy*`XH28Tjh;#C{ynX%p!L?Ig0Yf$O%)(nIM+XdoZziH(sCA?Thvs*<(OXXS zY`5M#2?5L9c?#X19?LCzC2Kn73?z!uy754&%ovbO15^=EDsq4i1OP_AZW+z2E zU&>azk*wSy5Dq!C79opR^j`w9>0$;b*X18zTL-D@<-QCnxz~>DnBx)I3#Uqm=-0o}R5YZT+U++qYfyX)kylA@P- zjCEhkkXAz5v#eM!t_bi-(!=Y;<&<_R`=>$5m&Cvb`Ma|hd@)cfc)}1T`ETFG?0wDbzu{X2-WTr#1R@!y=}twoMBn|ThDoOifhxgEYtse zuEuO(^+v=lu!omC169EI=Te_%3i~;#wnFcWLDQfrt3>KTsM6AjPrjEVU>{=amu3jE z(lR1>u;^WFFmsHkQ+5{mS&)qCHwnT_@k|_KSmJkq)nx;f2tW5T*kY!HXXE zvdyU!E&Ca8oBnN#;7B?;VmGrXg=k!!ba2)k$ZzrUMRmv#nu55xXnN}T!4=9~YHT`7;9ZJ`WOibSWF{rtu?biWL2S=@G zuB0z~z8OL(Z2S1Tk!B2NUNh<9jqymv-SgmtpGV1T)pM3VFYu|TkmN}E2JZBH@dV;N zTNqnp-%YtGDg%fW`nej5zx`QG>!AplZBJ3%Np~Wo>1PeYoRtZrL&So8&A)>7P^EUo z9e(XBQd}L6F{tPR{M7oZqyBB9z^d=aF@7<9$y-nY*9}OE`x_Gy8v%t|&MMxBZ{e#J zHN{-Dyil{K@qJcb9=F70)?QgA)8ybZ31Pj1k6%~6s53(~Ap$fg9=XC_`00z{fd?u(h|du04zcQg0%qP8@7 zsZk3;@4>_OM4eZp!%dLRfi>J0t0*c@kZAcOP7=U9#1R;e^(9r((%3i>E>))MKHmMV zZ`G9_dX-mT=rJ7DD33<6n|D6b^D4mYEGO9iJt5RsI}IG>r%x=)1OoiGl43F)p`;LC z02HQ`a2Fk(=h9M{YHtD_4-O8NvaBR`@WZw>GTMVtpF!ZT=5GhW*7B4DfezB5|Lje( z@4lpZNirfdCpboi?cTuK+VwE#cmEN^M5`JiLe|1Q=$tQp0H-vzqtVZIR@j!eW>1CX z)6Jdv8o>_fA>fN;KsG2I2xp~BjUp@5MZL&)xZvd;(+n$0+R-1g_8Dqf=VuIoozn-8 z=VjUo_XDL?jF0=~8$|OOod|PcSUC+l!0L-yh!CI&D3Wh(BXF6reQHAg)3g-HQESRK zg)l8lyG8NM^fHCX_oJvJl^`ylnBI>ruShPv3cx=IA}ZyPoX9iaHIV6pel$Lrj9n-3 zMW=%m>*fEgxYWBt9}uwUBbcw9`}z!X*wr$J#v?7UpKeL7qM5N(PlAq*ssaB(J^xNN zT2#m^BjFy5tqY_h!^2d?q`%Db`^h)czMACj?rzPKI$cJ;0qDEt-abGeVo~CdI~Qxb zT32|10yER52_)^e8}KODqr`E%CmG`R1}xeRPPDGaj$IdbZ%PqFR#Yv!^Lx7)zs=nX zU*4BqeWMcmK=pywKq4K*?zsneKqx&Nr;?K{oVqP$QHD667=OcUd$EStLvygi$)?L# zflz(6$swO#*fOO+TATd$+)Yw9Cdck zkFj}d#4Xpdlhl2n+Zm4Pb00tL98GMB3L`*RO-8W|M4OaleCIS98yn+_FM*oDt1t~B zu7U@n;M6H`UNMc zji{`0$L13(;SPfP<|^#DU_F39MCz1^J@4G<{3#gvv)3*!z!|Y_4N$KX z#WQrh$8t7D$|h!Zvhh%+ZlcC$VF%gT>~Y94Z(J^xn?YR{*^gsGybfzqv4 z3{sMoO8~Y43vZSY)(O;^z9$?++{+H-@87>KDJf|V`jw$u&hnlJ^xw;%Ad;;ARdP9( zik}Z^M|Fi>v68)Wb$l1oRJO1YkW-hyI;2dS@3`*)7YIAdX(hF)2c4r|W$ZH($Y1uV zlTYHEG2r32I1!5%@YTO%--4bz#ZBAwhzIW;pN52&Hceb88%G0rf^7U>g6_6_l(l@& zQXrD?uNo?&mhkq*iKR-NNRcN)SO1(l)q&cDYLnOqYG`cCJUEhy>SP8f?>0uG&ik;l zbTK)+rV1sr?y6m`79oj~ZJln3ubFC33FFk&395ZeC|eNKri(s-=l2P~+=#4%u%L%B z&%9}6zEYmG0&fcbqW9UsV^hMzAnl^vR=XiMwN0w%Nk}`1>U2#WBZXt6!k*jq3Jo?y zCjDDdKD~=yb^3$%$iHK2JLr=W1?S7s84r*?YdG zR}t77YipBl-aF>4r;?#VySa18>^et8Vr65Jy>QfjLY5Hm`TMiy&4{TVAJ%&Cxg7GM z`9O(~?Zr=Q?CROiw{%%!nn)tY`Yjyxch(3LK7018nffl7mtuG|(GZQ}rvZr00XW@?i?jyT~mTPTDP7`zezA2GFTdVI7>B|{_%wJ>M#T%8=GU5_9)0}y zyuNyJa#Bx1xuRq0apNU6w#v7&pjoRz$3LO5rp!NK(gc1%lEUf=9yNq%&@jzAYGC_#aY8cJMpm2|?Z(m#qKH1J2C}sNgX5r& z8nN$~9U=2(*!z2BDO42 z0}XG0j&$AfXcsXo>o)hyJJH{F_j$_g*JD-|(DWgpKe5%(k*+P`n&ekTu+|X1Ga(zG z?85h9vZq5DT93}1@{9ABg7TkvCiB1F*f6hC=IZ1-FQdh@O(gH>arIE2^92oQN(h>a z{QPzh)M;uAVYG%&PG`@XrqT<>rMXvGSs5PF5-&PF4)f1bomc*$vOv2vaObdED#n4s zJt)Xf_D;vs7^m+>Y+G-rMe%H2^O7t?JDjy|ivxFLWTf&d=s>S`b-Zk+7ZM%dKWnh= zDjqiQ^IVEq@<&N-NKj_=GA=stvAWB$i#5T$D5 zNb5(I&$mz9D!o2c@i_QGkLjVQI%yrWJ%hFrAv}8113wK3#)TNjP|cB6#s(m%$B_ba z<&Pytcmm205WSgj>P;&*-w()6yAaHaz&TV}sNcOyh(y}ClGnO86JZ5RQ(1B>G2{u8 zycC+($0Bt&9IzO5za-61pbprTz1eT0XozUQ7UiB6<>7OH+fPP4AAd?#Oo^e%jK}IGn zrx%YIRF2VmSpCu%+=lPiQxGsXdjx_}YOo1}DZ0lf^gWTztYhft?Ejt&s?h{h0pCzW zr6hAiA|t;kHQ`#XJEL9(8goJVYmS8UfP%Z3EdWnA>h)_<;X23C0qm>1RF;Q4;Q%)I znUP+zuMV{=0_s`z7v<(n-~1Niigu|wKPpsB%RGFM4NNBw$8~41U}~WIy?{ED{RFNP zy3z5h27`qAT#+MK2+5Kt${qzvs;bH=< zn4!UY8z0U=K4D)Bb#_C+#mBtpnC$ac;Bvg^{@K9hkmSsQw~J7lWqm`^oF)^&rFV5? zqIF~4CyL0I2X=`rXi3o#5me69fw|5QIjQaIKGr@$h{@2X`-WJ6Q-9pW&KNm_Wxd}O zGA7$hJ!b2sJKs5m%$A>K+Mh?hNZIi`XaEIFvpEaq;r&UtO@ObZe0o>{JGVf+tz@fY zCA|FY^hG!}D}6{viYde1aA7YUF1`?X@-MKZwfd=cni=42;GWogM9>_=h6=wn*N;g_ zHp%amHOs$d<|qWxiD?8q+q_Ht`WD&h<_uOzitMF29=`89wHf1F(cJy6+@0?3ZoQ0$ zc;T>g6L_mii^>G<0GJMx$V%XjigFk*CcQcCNg{q&Gu9DI2$0)%^|n+khkiS~{N%KI z3SHOgVBO3A$YtHE=#1&`0o5!dPU@OA5d(n9UP};wl z-2}owWWBcB`zx^_=>GF{ zUoRFYI}LZmdDV;%XaTJ}Z~?AJACa@%8%T7O?qe|d0ld|0v`V(C9L>Nz>rua$?Jr(K zMRUcQln;~yyt^lOVrU!Ydxfhy~8IJ7UM;6aBve=tw zFgdbyhIi)I_kST2KB4(UaQ`5RwH+tN&LPt4;ugjk}N!434+ z?D@aDs%XXmDH)ruUT8gLQ2yUe(eQ#)2I!hPxC@(FrgnSRnLx)`7*EuPczj;Fuxb`+ z?T!X3Sa<&#StQ^)Fq#chkJZvTz5C!VUsBv_~+iREsmD`h2-oN0e#Z{izsd-D!ih|;vvEV#RIr6{h=@>U(Bg@f z;z0QHGqJMzZP60i|GcR!hO!MH*+7Vr>^vOEuc zFBj2a#kwM6pz{C-*2D&W00RVMdigsM!sfS;0yq*_pqHCJOjZ^Kc=VoWLb)Z`DRbdD z`Y;Gq;QuaR=iGNl*QgNhvVdtW7-Yst1PW;R`?h+Hp_P_{I`xY4gLXB0yk41XaW#4aT^~j^0%PK z9}$_z$IueJLmC;&*yuJJCiNOw`cQb-}2XU@M4o zU1^?PamN%0J%vduua9_wQA0$P!RIZo$2sr-lr+D714F5>v@lH|0KM1AY}XBIysod; z5W85b&JN>v((S^95?nUdcPNQv4CIh7K}v zGMz)ZN(u#s7E9hmfv=3xnZ^OnT&am0!4@&;FXFFpi)S$b+IsE$=RY@j<^Oe) z=flQ!@)<7=k2nOsQk}t1B&dk9GHF9FQ9vX2b86~w7fVQ0pUp#C4L~WYz)s^e;)L78 za+qTU`z(jowkqvE{$Hns6R(gFX2txWykzD#ds3JAx#Vxa;`6x@sOQa|QmUAkn2_y0 zK^3<+QsZIs)licJ!S%et4S6ASt4jrkm?+8_*W*Rwyw^k#c0KVKF;ie z@kfOnaBOIQ@T;FDh$&VjP3XaXI#S53S8)LS363`rPFSQwitX zT#tS|T2WcK?W+%Oxa`yaG0)-F^R36~ew1CzLL(-<L0opDou2PgPG(@6q6}kh@)AK)~3pyE)g{^i498tpg2>je+Zv zaMXAZ?CRe}q)=ikN{G~9m#f6Lw29Ph&wK3o&+Iny(dN18UPVU-5HHo@!)&;eVO~BE zbiEV~dSnX!A&mmxar@$op6A_$Ic|VrFnTT_y$IqN;Z(4i)pwl4^vaExy=|Y;I~4|7 zM$t}*^7Wafk$gF@T@lU*FK_Tx8u|i3Ad0~nN}vsYXi~cb{Ds#Fd^1pH6;_Dm#LXxR z@dsc31)i1X2)nryf|rw`3JbY)K}%Rh8k2zt*2!YKY4X=DC`~clq;3$ymPAp6p(=b2 zn*~5fHw>L~r$47FtE+F+!it5XOrzTh(bFdU2W3Iq7L=YqdWmS6tbYMej>8|C+o=;N z=8~s&*#mPW9Cx^QG6M-M2s{*Jr@-5v&#M3BtwBYB7(1V!!9nory(!gheuU5n{D zynKCDHMEGnZU{-#yW{HcaseuWte?c99_4PJzcIK$)&?W4dMOn0 z<{W|4M@4J<=hdrMplLrUC0{-F;B58CB2WO-4WK|o->m-m^vyGc0?;oAaz_o115L*L z9h@cng5m71qddUaggyn7{%Fk}L`ezwgUAM2^pCA@xGWRdQPvlKw{~FkG!sx&Q6VQI zBX}9W83A#C>a=qnRvmu_jXBr2bzI{L`Y!YiY}MY^r8@zQ1UZT@Dhe#@e{c!bapWWX z3UH25@-@-F%<^?sL()NjVPFU*P9Zzv`!$oi6osSapmaS-ifpSz8D|fE7`r%dWEFQ! zXb~cu7ep_(2w|HOvVD3Q)A7Q>!eGJ$uMUrdk&#jSty=6S5xXhn&ggyrJbeJlB;bn- zAhQQjGm6$DfxWu8k8q=Cf|J>@aO|z7MPUN27Yh|T*t@i}L_#lKf?c??wdTX}h-UrA zSFW4&=L>E`8$|&YrFGzO!x8rHjzXap)}~6Io7$j6Cj;FU;j%QG7qp?3ZLg06fqQxF z_6$269X;WKmULk+ic~QQ2ihpUzWLL6Niw+Xbj`0}MA;7=e@cFnHpG}FH^0+bjjlK~RO*w`3>1EL$T zN04+`vw-zCI(mQr)Mgp?hj-U9LS2e20KTLG2tP zzx=5+4N{`ge5@`99U>jrI`r9uZONgMC?}L$Wt*4y`3Y8CRD5WgqYCgYePWj1+ub^Z zje(N|YF#zT@NVxQMdNAda1W~De!AqIJ(Ju`oJ|@b>qie~Ky7V9dqGXUh|Sn*mghYf z+c`b9TcgX28mAMXOo*e#i|EEu{n>!(!*AnYiy;7lweU#QH?@z@)D$h@;jS&hKpjvG zh%el5YJUfK_*Hyl=cKki?jk^YX%eBK8JjMf5t%?^}OXUyDuW&`4Xw ziE%{yrsla#S#YdZ`!5e@0j(Z6zCJMfy)+!cotEEeG2xFQrYUHM&uf#C>%mvxV>nyr zy=th$UDqLXp-CzLj%mXcNvKDL@JA?!E=uzr31eTMe_Lbv_a4C%TMmK_^fsfN5BhZ4 z5G@c>2cIiU^@QKPm*mHJFHttxG^N0qAQOY0xu+# zDnYLn3;#d|?co?_NhVGT!BF+vUzIu?Is`z~2EHR=)pII_%phX5-@fAc5omBndgDK& z-w2>u0c9fwX9?+hvZj8VECM+f@Lm!>Id)~8f!seyE|R>#O-4qBkQ2~uAu?yW#dw_~ zY|v>UN=rjqa><(gax0*Ky$i-qd)(MGbUQvlPkoiR_11U;@Gm zC@jM9WH>OOHZmABzvnTahYO^8E~3)SiD7w|eSm(l04)CSH5bZiZwX01y?V~qEdC|9 zWrB$kSTaE4mYl4t>s;Xkl}=m)Bj|ce#nSDtzK{*;ADEJoKUH+_X9U`V8+ho8t zZ8eMkiMUu;LP_wy%az5TAuOdv$%Eq&PdEIu0F5Kw@4NJ5(rc=#qv5}Lx?hp$O}HU| zFTq5Ip2%qp-I!y3(mWU(fB>INH&zh`T{9i!1C*cuzAX9p7Fkql*sUxqERQRaoCt=z zN*PH|q82ZiBZGr6&3MNW6ho|xDW5bvVuRR})hvcT#z@|3FH?LwJb;o{$YwlMr?iNn zL93hPw7r{kib`69dC20{j-G^P@S8VrUGNa^Jn^DXCCLOu2^J6M7t2nEBY{&-%qmWbJOp7147wHeAc2*5 zE2M7Xl1iki?P=Y;2q7E7nnZ4QgF!n(K_M8U`c&^M=5}W2 zN+c+CI|eB!Dp~>K0^owZ;S>}E&y~rrRz0zVT8J?15T+7zT&2oar7||o+N0~`55aep zZBUO2X-d&hoRu{hd)@O82_S?m2tP*t5U^Yf7%$aJW)_iXz@v_`G2+n_2?a`;ANBQk zCABJh@zqvaEiIZg6?H|lt}LVUMSIL|FkE(Gm1mA#M>z=!k{c~%q>MMvqos2Gwe+J5 z$hKw}CTDr&c;&*zHo7UQbsF5+i|ylOxq@nfV`5@3j&OT@km2^FNB`{&WWWKKD0B(G#Twl@>9uSI0mjNj6)mu{W?0D zZb#^2^CzQq7*>&O--m`0KJC{prB&GnDZNK97gsIyze*pq8y-kA=6B)RL0P9Omdm5d zbV;A%h4k^-@U*2ntfTb?fBeU1NSC+7pK&r$G3Y14g2x zds3nY+o7OEpRaBKa<`&Y=$$j@&7h}`fsy+C`@ZcO zvi)5H<4nPRJB$WH| z23iR)NcRmqVx{mA)niNnu|~POo0-|=6SRaD`l~hsGX_|je;>xzp`uUMDm*1cmV9;JYO#Z z2VsDt1M4F=4nzI}r#z0>{-?T+PyxHs<*Z)LIK1{s+l}g6b%+bOvrXF>Z7?V0*R(I5h~? zC@|w2_rxoU-T5ba0JFSV-)-PFQP;&6d8AA@+JQUE1X347?f{PX084>B8OuYd$!&>s z|NoXd0lnu8oCNBVV_>)BgRV$HC4o1Q0RaX{fkKE@B22`g5aJjL9DrdpE)#J$0Mlw53UQfBHwO$Imr@*5 zbPH1|xYU9LEt3t_^g{)|5?OlS2!WxKfQODtM^K~=okD@q^go*XhzWoTINf4yyD3#Zya;PF>sZcGdZfOLc;1A{g*j7mrao^Zj#0HqqHGJ<#@Y6Gz4 m6~+dm9srLxX88Y~nc->X{Ke;{Z2Sf)SUp|+T-G@yGywpo{R&qA literal 0 HcmV?d00001 diff --git a/SQLClient/SQLClient/Images.xcassets/LaunchImage.launchimage/Default-736h@3x~iphone.png b/SQLClient/SQLClient/Images.xcassets/LaunchImage.launchimage/Default-736h@3x~iphone.png new file mode 100644 index 0000000000000000000000000000000000000000..7a49d42af635f449a28870bcbaff4f2a284a74ca GIT binary patch literal 130053 zcmeFZXH-*d*EJe%5fo`k5$QHSx>V^mBB1mt9Sle(bO;cNf`D|DUIGfzo1ugfih_WI z-b)Bb2|XY+)bMS5&Nn!hsL0{w;cDyPYy$%MPQmr;^z~L5WC=5SPt@MMPE>Q%rN7Fd`y~1`-3Lyd zJJ)Db-`)8cxRsCe&u2LjF6VVZSO;PlO?Z&Ur&L16fivoYYA;0eiqPbf2>W=5xy-T3O zAQ>6nyWhx~L6>}ouU`dy)5!QH#1e5%z4j!{?9%JEm%e#@c&v8yH67@0|F|dlpud$b zy@sc9>VZCyfL?Z6TP=XJ`9Uwaf|rItui=MjuuCA*#5?qtK79aj(AvCv3bL05mG-}g zQUVzXf#_`2en^5Q_(4L?4Q-!+zSe@i_0e3f1YM;73F*8Gdk7+dfnNT+ch48}HVs6l zykjV{&09{h%mW~mRw+||UsCCf=~aGrGDAZFHtxRXw|MEq!GD3%{}yQVOS>)nMwI&3 zZVw3b=>t9Rw7X}%eN^Rred2LQDog&g7Si*(R#po{Tz`q%-yqPOSI{7lPq2#VwKVB# z2r=iuCW*bp^-tb}XxmEaziU9BR;LVi-7c_E`ux6rauT<=IHCIEi76Ou7)Z2hwftsy z=6n<+b4u9%)jWUyjj;I}6_UeWtv!3{c`W@Ouf4UJUW!*at-XGJ$~t`a$8)QCBOba1 zoty4)Pt#*|h4b!4KKb~t{lU=rtNB^Jlgo0mVxX^%a+hEq_zTUgFCVCJ(HMDsnxZ6i$)BlwdH{ND`~0Ij2xRf}zJNZuO1|?d2=w&R8=kKU%zNJ% z`M!~|e7`dN{rc%k@wZRz|7d+e^Mw4>YZi}}4@*OyaEJGPW#xG(IPy2E=rEDdpCI9o)9^->M~QDLb!0~fEl<$)4=>Zy1+#ZIAbD~x0!Kb(aGI@+Ef9`jvGuw zVEX!^^fD@P-?|?GCAs-V>Dx7N66g~SFq^_NBXuLqiD!izEcZokOkI<>cKNMNE7#qG zLbYnnh8sse7_EdKMHoHfyEk?-o>q_s_D=DK#QmF*io#s=@Bey^_@X~_b4Yo}Mfct+ zUr~nWGp2h9T~n`|D#;`h`8arghAqMu+ZII@nHTTun3BV-{&xRzs5j58i*{7KjibSd z;z(^$g%u2P({-_hpY?Bu#hW}S`;64B(1|Fd74=S+>`*Y%k7?24odBZ}<`UG?R^1R0-2k{Y+Ew`nPU(fNe1TxjgzxNd#B%9rns6J<%b z_ZzVvoQU4Z03RMy>KQ94TDG|8v!tSks)%8g?rx&3JlR63`(dV+=Cx(+f|T8$s2RGg zhw~4a^F`I`AK92W6(^@88SVHRk_T#RP5sWuE3Y| zCiSodw^w%%4$Dp)D>2o2gmN7O%6^T?=OXnwF1Q0tL_X&mhMI`zjPh zq*WC6Nih_Cj56(F?yfSdf}NOc9YyoDzZLVqN#K;Zw;$>UWpYQAW0k3696HPv?k_+( zARW1!q?~s+)il;LHq&)A>hmAwORGz(clP%5=JbxGiDk$>8W Sk4&Fn5}(j;%mZ( zEJW7XJg;0d$v56aHoAnFNE+uE8JL(?DVM>^9*m*NPz5&%>NOKIOR~o7+P-chyO2e- zx4+^hNHOI|2r>|CXLZZ2w!OSV?!ydSB+I zd|>-t@~WVnQ9n)pgvbJtw)^1^2e@9wu!`L+yL!*i-FMNEY9ZYMDRKfC%pKUDO^d2t z`|jp*{zLoljH--Qd?BV{Q+(55Gh#ZsM>P9kma&NEK-*8nUplx!`Rmz9LQb=#L0XDh zZ|rK8?+IE9elRQ>|5}>|Ehd1sZpj_Ud1NohEyzgB2+tT>_DFoI{aM?s)#jRI88)jJ zeDD0#x&QefsNnU3OLmtd-zdB>{p;k=Kj2PFoV*mq=Ad#CuMqR8UV&aAKBO!J_r2_n z{m+f}Wp@b6seK>%9H_#ygI2}{$iPeB=(ZpVuCRzO!*KaHw|7QNgO5_gpo7zjp0iJn zpC&(@$*_B*C0W6*D#jyMEZ`_JCD9<-Am1TiYuqzx1UGu_ti~cKz*8>n;@muYir84sK0&NChj|Bo9V8pZtXw~OTQ;p*RV6}@j%r&PygNM$jk`*1*nWu=-P zg?{|--u+4O9gPH}-J0E!txtEUO;{zd-6gtKkS&R<%c<&p=@nysa^#cz6@3%#aUQZt zI~zmWmflFTWO7LsZ!f;53XOR$k-3$9j=$M@bM7NDFI>%#pVerosnk)jQpVj6L%u+N zg*W^KX{k(I6p{xrs#StFOeqg);`G;WdSme&9rC^AlGFNi19Dtg;QI79-dxK_(0H~6 zd>jsEmZOi1DyPHTN}&&);GXuyF-N$fThgWztBXp0)W7Pt9kN|rn&HnfrAjVNSxTXD zp`4tmrks6Ijo&{|!U(4Ha$``TNX7cx7MZ$6n}V*sUTJy#FZz4>$NJ)%L)aSd_@T?- zCNr$@Ky+giW7IeC6kMUXF#EM7fKK7p8*X+qrbotK9$pOM|8 zLXOvujIs{N>g&dj@vf^-b+R`T;agCCN^weq*vyXkdu~tt7FP+^W(jPRQd2%Lu+llQ zVRj!@J61u*C+R!s${Bj;C#F`W4O%^NgJWwm{--J!cIr$~8Dl@yUHvYHsnw9-1iPL7 z7kQK?q%0w8 zUTZSMgkh~tR3<9Dyxz>Ie$&6H24+iGWzLf5!|pZtZ8MyYjb=&8Vw-mTT?y@k{aFTV zkO$XUUpHzJ%C%Y6sMTn>_cvZ)QV|<5DsM`hp4gfWcKinYv^zMhLLdzb?I(WNkEd3= z5}y{|9Zng(p7mHxQgUA2<81Y`K(YT-KWEl0Vh*vp{1$^?(BbI%c;8^^;OCT`l!O*Y zK;GHfxAof;-t=w5^t;GP2VD5AKBgDSmK*34E?SzDL zlFO;Pr`96;7{G6GSU=O%0D=4;fIzQ9L7<~^;QJ~F`z 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 Date: Tue, 25 Oct 2016 11:23:10 -0400 Subject: [PATCH 067/127] Updated readme with testing --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 01010a7..5d335a8 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,21 @@ SQLClient maps SQL Server data types into the following native Objective-C objec ##Testing -The type conversions have been tested with SQL Server 2008 R2. +The `SQLClientTests` target contains integration tests which require a connection to an instance of SQL Server. The intergration tests have passed successfully on the following database servers: + +* SQL Server 2008 R2 +* **TODO: add more!** + +To configure the connection for your server: + +* In Xcode, go to `Edit Scheme...` and select the `Test` scheme. +* On the `Arguments` tab, uncheck `Use the Run action's arguments and environment variables` +* Add the following environment variables for your server. The values should be the same as you pass in to the `connect:` method. + * `HOST` (including port, i.e. `192.168.1.1:1433`) + * `DATABASE` (optional) + * `USERNAME` + * `PASSWORD` +* Set up your database by running the script located in the `setup.sql` file in the `SQLClientTests` folder. ## Known Issues PR's welcome! @@ -77,12 +91,11 @@ PR's welcome! * datetime2 * datetimeoffset * time -* OSX support: [FreeTDS-iOS](https://github.com/martinrybak/FreeTDS-iOS) needs to be compiled to support OSX, Podspec updated +* OSX support: [FreeTDS-iOS](https://github.com/martinrybak/FreeTDS-iOS) needs to be compiled to support OSX and Podspec updated * No support for stored procedures with out parameters (yet) * No support for returning number of rows changed (yet) * Swift bindings: I welcome a PR to make the API more Swift-friendly - ##Demo Project Open the Xcode project inside the **SQLClient** folder. @@ -123,3 +136,6 @@ https://github.com/patchhf/FreeTDS-iOS FreeTDS example code in C: http://freetds.schemamania.org/userguide/samplecode.htm + +SQL Server Logo +© Microsoft From fffc888766c5cfb4e6895450526abc01ce089ec5 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 11:37:28 -0400 Subject: [PATCH 068/127] Added nullability annotations --- SQLClient/SQLClient/SQLClient/SQLClient.h | 26 +++++++++++------------ SQLClient/SQLClient/SQLClient/SQLClient.m | 14 ++++++------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.h b/SQLClient/SQLClient/SQLClient/SQLClient.h index 770483d..d54eaef 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.h +++ b/SQLClient/SQLClient/SQLClient/SQLClient.h @@ -8,11 +8,11 @@ #import -extern NSString* const SQLClientMessageNotification; -extern NSString* const SQLClientErrorNotification; -extern NSString* const SQLClientMessageKey; -extern NSString* const SQLClientCodeKey; -extern NSString* const SQLClientSeverityKey; +extern NSString* _Nonnull const SQLClientMessageNotification; +extern NSString* _Nonnull const SQLClientErrorNotification; +extern NSString* _Nonnull const SQLClientMessageKey; +extern NSString* _Nonnull const SQLClientCodeKey; +extern NSString* _Nonnull const SQLClientSeverityKey; /** * Native SQL Server client for iOS. An Objective-C wrapper around the open-source FreeTDS library. @@ -30,14 +30,14 @@ extern NSString* const SQLClientSeverityKey; * To list all supported iconv character sets, open a Terminal window and enter: $ iconv --list */ -@property (nonatomic, copy) NSString* charset; +@property (nonatomic, copy, nonnull) NSString* charset; /** * Returns an initialized SQLClient instance as a singleton * * @return Shared SQLClient object */ -+ (instancetype)sharedInstance; ++ (nullable instancetype)sharedInstance; /** * Connects to a SQL database server @@ -49,11 +49,11 @@ extern NSString* const SQLClientSeverityKey; * @param delegate Required. An NSObject that implements the SQLClientDelegate protocol for receiving error messages * @param completion Block to be executed upon method successful connection */ -- (void)connect:(NSString*)host - username:(NSString*)username - password:(NSString*)password - database:(NSString*)database - completion:(void (^)(BOOL success))completion; +- (void)connect:(nonnull NSString*)host + username:(nonnull NSString*)username + password:(nonnull NSString*)password + database:(nullable NSString*)database + completion:(nullable void(^)(BOOL success))completion; /** * Indicates whether the database is currently connected. @@ -71,7 +71,7 @@ extern NSString* const SQLClientSeverityKey; * @param sql Required. A SQL statement * @param completion Block to be executed upon method completion. Accepts an NSArray of tables. Each table is an NSArray of rows. Each row is an NSDictionary of columns where key = name and object = value as an NSString. */ -- (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion; +- (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nullable results))completion; /** * Disconnects from database server diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 06b6431..f61dae6 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -88,7 +88,7 @@ - (void)dealloc #pragma mark - Public -+ (instancetype)sharedInstance ++ (nullable instancetype)sharedInstance { static SQLClient* sharedInstance = nil; static dispatch_once_t onceToken; @@ -98,11 +98,11 @@ + (instancetype)sharedInstance return sharedInstance; } -- (void)connect:(NSString*)host - username:(NSString*)username - password:(NSString*)password - database:(NSString*)database - completion:(void (^)(BOOL success))completion +- (void)connect:(nonnull NSString*)host + username:(nonnull NSString*)username + password:(nonnull NSString*)password + database:(nullable NSString*)database + completion:(nullable void(^)(BOOL success))completion { //Connect to database on worker queue [self.workerQueue addOperationWithBlock:^{ @@ -168,7 +168,7 @@ - (BOOL)isConnected // TODO: get number of records changed during update or delete // TODO: handle SQL stored procedure output parameters -- (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion +- (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nullable results))completion { //Execute query on worker queue [self.workerQueue addOperationWithBlock:^{ From 11aaddf2bf27985fc57de08a0f12889ee46fe789 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 12:36:32 -0400 Subject: [PATCH 069/127] Make text view not editable --- SQLClient/SQLClient/SQLViewController.m | 1 + 1 file changed, 1 insertion(+) diff --git a/SQLClient/SQLClient/SQLViewController.m b/SQLClient/SQLClient/SQLViewController.m index 54eb401..f2e4d07 100644 --- a/SQLClient/SQLClient/SQLViewController.m +++ b/SQLClient/SQLClient/SQLViewController.m @@ -42,6 +42,7 @@ - (void)loadView //Load textView UITextView* textView = [[UITextView alloc] init]; + textView.editable = NO; textView.translatesAutoresizingMaskIntoConstraints = NO; [self.view addSubview:textView]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[textView]|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(textView)]]; From c1231d940442cc0cdf0c548e335bc4a53d34dcc0 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 14:37:22 -0400 Subject: [PATCH 070/127] Renamed FreeTDS binary to libsybdb --- SQLClient.podspec | 2 +- SQLClient/SQLClient.xcodeproj/project.pbxproj | 8 ++++---- .../SQLClient/{libfreetds.a => libsybdb.a} | Bin 3 files changed, 5 insertions(+), 5 deletions(-) rename SQLClient/SQLClient/SQLClient/{libfreetds.a => libsybdb.a} (100%) diff --git a/SQLClient.podspec b/SQLClient.podspec index 1a4a20e..828219a 100644 --- a/SQLClient.podspec +++ b/SQLClient.podspec @@ -7,7 +7,7 @@ Pod::Spec.new do |s| s.authors = { 'Martin Rybak' => 'martin.rybak@gmail.com' } s.source = { :git => 'https://github.com/martinrybak/SQLClient.git', :tag => s.version.to_s } s.source_files = 'SQLClient/SQLClient/SQLClient/*.{h,m}' - s.vendored_libraries = 'SQLClient/SQLClient/SQLClient/libfreetds.a' + s.vendored_libraries = 'SQLClient/SQLClient/SQLClient/libsybdb.a' s.libraries = 'iconv' s.requires_arc = true diff --git a/SQLClient/SQLClient.xcodeproj/project.pbxproj b/SQLClient/SQLClient.xcodeproj/project.pbxproj index 36643dd..72f94d5 100644 --- a/SQLClient/SQLClient.xcodeproj/project.pbxproj +++ b/SQLClient/SQLClient.xcodeproj/project.pbxproj @@ -22,7 +22,7 @@ 5B0FC87A180DCEB000DF4EFE /* SQLClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B0FC879180DCEB000DF4EFE /* SQLClientTests.m */; }; 5B0FC888180DCF3800DF4EFE /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B0FC887180DCF3800DF4EFE /* libiconv.dylib */; }; 5B0FC8D9180DDF7E00DF4EFE /* SQLClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B0FC8D1180DDF7E00DF4EFE /* SQLClient.m */; }; - 5BC11BCC180EE4C9003471E4 /* libfreetds.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B0FC8CE180DDF7E00DF4EFE /* libfreetds.a */; }; + 5BC11BCC180EE4C9003471E4 /* libsybdb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B0FC8CE180DDF7E00DF4EFE /* libsybdb.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -60,7 +60,7 @@ 5B0FC8CB180DDF7E00DF4EFE /* cspublic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cspublic.h; sourceTree = ""; }; 5B0FC8CC180DDF7E00DF4EFE /* cstypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cstypes.h; sourceTree = ""; }; 5B0FC8CD180DDF7E00DF4EFE /* ctpublic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ctpublic.h; sourceTree = ""; }; - 5B0FC8CE180DDF7E00DF4EFE /* libfreetds.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libfreetds.a; sourceTree = ""; }; + 5B0FC8CE180DDF7E00DF4EFE /* libsybdb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsybdb.a; sourceTree = ""; }; 5B0FC8CF180DDF7E00DF4EFE /* odbcss.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = odbcss.h; sourceTree = ""; }; 5B0FC8D0180DDF7E00DF4EFE /* SQLClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLClient.h; sourceTree = ""; }; 5B0FC8D1180DDF7E00DF4EFE /* SQLClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SQLClient.m; sourceTree = ""; }; @@ -80,7 +80,7 @@ 5B0FC851180DCEB000DF4EFE /* CoreGraphics.framework in Frameworks */, 5B0FC853180DCEB000DF4EFE /* UIKit.framework in Frameworks */, 5B0FC84F180DCEB000DF4EFE /* Foundation.framework in Frameworks */, - 5BC11BCC180EE4C9003471E4 /* libfreetds.a in Frameworks */, + 5BC11BCC180EE4C9003471E4 /* libsybdb.a in Frameworks */, 5B0FC888180DCF3800DF4EFE /* libiconv.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -176,7 +176,7 @@ 5B0FC8C9180DDF7E00DF4EFE /* SQLClient */ = { isa = PBXGroup; children = ( - 5B0FC8CE180DDF7E00DF4EFE /* libfreetds.a */, + 5B0FC8CE180DDF7E00DF4EFE /* libsybdb.a */, 5B0FC8D0180DDF7E00DF4EFE /* SQLClient.h */, 5B0FC8D1180DDF7E00DF4EFE /* SQLClient.m */, 5B0FC8CA180DDF7E00DF4EFE /* bkpublic.h */, diff --git a/SQLClient/SQLClient/SQLClient/libfreetds.a b/SQLClient/SQLClient/SQLClient/libsybdb.a similarity index 100% rename from SQLClient/SQLClient/SQLClient/libfreetds.a rename to SQLClient/SQLClient/SQLClient/libsybdb.a From 3b64a311f7b1b1ebf874796e07befb7e7fc4b6e1 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 14:38:25 -0400 Subject: [PATCH 071/127] Moved client variable --- SQLClient/SQLClientTests/SQLClientTests.m | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index b74435c..ace9e37 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -79,10 +79,8 @@ - (void)testBigInt - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion { + //Environment variables from the Test Debug Scheme NSDictionary* environment = [[NSProcessInfo processInfo] environment]; - SQLClient* client = [SQLClient sharedInstance]; - - //Create environment variables in the Test Debug Scheme NSString* host = environment[@"HOST"]; NSString* username = environment[@"USERNAME"]; NSString* password = environment[@"PASSWORD"]; @@ -92,6 +90,7 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion NSParameterAssert(username); NSParameterAssert(password); + SQLClient* client = [SQLClient sharedInstance]; [client connect:host username:username password:password database:database completion:^(BOOL success) { [client execute:sql completion:^(NSArray* results) { [client disconnect]; From eb3cee5afeef06d2f8dc37bcf99987e25b2842cc Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 14:38:52 -0400 Subject: [PATCH 072/127] Used macro constant --- SQLClient/SQLClient/SQLClient/SQLClient.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index f61dae6..3dfcbfe 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -553,7 +553,7 @@ int msg_handler(DBPROCESS* dbproc, DBINT msgno, int msgstate, int severity, char //Can't call self from a C function, so need to access singleton SQLClient* self = [SQLClient sharedInstance]; [self message:[NSString stringWithUTF8String:msgtext]]; - return 0; + return INT_EXIT; } //Handles error callback from FreeTDS library. From 72d88a6b7c60234a82e4160c7385b7c5405246b7 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 17:01:20 -0400 Subject: [PATCH 073/127] Updated header docs --- SQLClient/SQLClient/SQLClient/SQLClient.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.h b/SQLClient/SQLClient/SQLClient/SQLClient.h index d54eaef..f1720ce 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.h +++ b/SQLClient/SQLClient/SQLClient/SQLClient.h @@ -33,20 +33,19 @@ extern NSString* _Nonnull const SQLClientSeverityKey; @property (nonatomic, copy, nonnull) NSString* charset; /** - * Returns an initialized SQLClient instance as a singleton + * Returns an initialized SQLClient instance as a singleton. * * @return Shared SQLClient object */ + (nullable instancetype)sharedInstance; /** - * Connects to a SQL database server + * Connects to a SQL database server. * * @param host Required. The database server, i.e. server, server:port, or server\instance (be sure to escape the backslash) * @param username Required. The database username * @param password Required. The database password - * @param database Required. The database name - * @param delegate Required. An NSObject that implements the SQLClientDelegate protocol for receiving error messages + * @param database The database name * @param completion Block to be executed upon method successful connection */ - (void)connect:(nonnull NSString*)host @@ -69,12 +68,14 @@ extern NSString* _Nonnull const SQLClientSeverityKey; * Executes a SQL statement. Results of queries will be passed to the completion handler. Inserts, updates, and deletes do not return results. * * @param sql Required. A SQL statement - * @param completion Block to be executed upon method completion. Accepts an NSArray of tables. Each table is an NSArray of rows. Each row is an NSDictionary of columns where key = name and object = value as an NSString. + * @param completion Block to be executed upon method completion. Accepts an NSArray of tables. Each table is an NSArray of rows. + * Each row is an NSDictionary of columns where key = name and object = value as one of the following types: + * NSString, NSNumber, NSDecimalNumber, NSData, UIImage, NSDate, NSUUID */ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nullable results))completion; /** - * Disconnects from database server + * Disconnects from database server. */ - (void)disconnect; From 76c3f10fba568bad092f70c880cac0a104a0ac76 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 17:03:44 -0400 Subject: [PATCH 074/127] Removed docsets. Cocoadocs auto-generates them. --- .../SQLClientDocs/docset/Contents/Info.plist | 34 - .../Documents/Classes/SQLClient.html | 944 ------------------ .../Protocols/SQLClientDelegate.html | 369 ------- .../Resources/Documents/css/styles.css | 615 ------------ .../Resources/Documents/css/stylesPrint.css | 22 - .../Resources/Documents/hierarchy.html | 83 -- .../Documents/img/button_bar_background.png | Bin 2825 -> 0 bytes .../Resources/Documents/img/disclosure.png | Bin 115 -> 0 bytes .../Documents/img/disclosure_open.png | Bin 131 -> 0 bytes .../Documents/img/library_background.png | Bin 183 -> 0 bytes .../Documents/img/title_background.png | Bin 177 -> 0 bytes .../Contents/Resources/Documents/index.html | 79 -- .../docset/Contents/Resources/Nodes.xml | 107 -- .../docset/Contents/Resources/Tokens1.xml | 402 -------- .../docset/Contents/Resources/Tokens2.xml | 57 -- .../docset/Contents/Resources/docSet.dsidx | Bin 217088 -> 0 bytes .../docset/Contents/Resources/docSet.mom | Bin 11879 -> 0 bytes .../docset/Contents/Resources/docSet.skidx | Bin 13312 -> 0 bytes .../docset/Contents/Resources/docSet.toc | Bin 770 -> 0 bytes .../Contents/Resources/docSet.tokencache | Bin 4919 -> 0 bytes .../SQLClientDocs/html/Classes/SQLClient.html | 944 ------------------ .../html/Protocols/SQLClientDelegate.html | 369 ------- SQLClient/SQLClientDocs/html/css/styles.css | 615 ------------ .../SQLClientDocs/html/css/stylesPrint.css | 22 - SQLClient/SQLClientDocs/html/hierarchy.html | 83 -- .../html/img/button_bar_background.png | Bin 2825 -> 0 bytes .../SQLClientDocs/html/img/disclosure.png | Bin 115 -> 0 bytes .../html/img/disclosure_open.png | Bin 131 -> 0 bytes .../html/img/library_background.png | Bin 183 -> 0 bytes .../html/img/title_background.png | Bin 177 -> 0 bytes SQLClient/SQLClientDocs/html/index.html | 79 -- 31 files changed, 4824 deletions(-) delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Info.plist delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/Classes/SQLClient.html delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/Protocols/SQLClientDelegate.html delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/css/styles.css delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/css/stylesPrint.css delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/hierarchy.html delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/img/button_bar_background.png delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/img/disclosure.png delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/img/disclosure_open.png delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/img/library_background.png delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/img/title_background.png delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/index.html delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Nodes.xml delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Tokens1.xml delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/Tokens2.xml delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/docSet.dsidx delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/docSet.mom delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/docSet.skidx delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/docSet.toc delete mode 100644 SQLClient/SQLClientDocs/docset/Contents/Resources/docSet.tokencache delete mode 100644 SQLClient/SQLClientDocs/html/Classes/SQLClient.html delete mode 100644 SQLClient/SQLClientDocs/html/Protocols/SQLClientDelegate.html delete mode 100644 SQLClient/SQLClientDocs/html/css/styles.css delete mode 100644 SQLClient/SQLClientDocs/html/css/stylesPrint.css delete mode 100644 SQLClient/SQLClientDocs/html/hierarchy.html delete mode 100644 SQLClient/SQLClientDocs/html/img/button_bar_background.png delete mode 100644 SQLClient/SQLClientDocs/html/img/disclosure.png delete mode 100644 SQLClient/SQLClientDocs/html/img/disclosure_open.png delete mode 100644 SQLClient/SQLClientDocs/html/img/library_background.png delete mode 100644 SQLClient/SQLClientDocs/html/img/title_background.png delete mode 100644 SQLClient/SQLClientDocs/html/index.html diff --git a/SQLClient/SQLClientDocs/docset/Contents/Info.plist b/SQLClient/SQLClientDocs/docset/Contents/Info.plist deleted file mode 100644 index b99610e..0000000 --- a/SQLClient/SQLClientDocs/docset/Contents/Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleIdentifier - com.martinrybak.SQLClient - CFBundleName - SQLClient Documentation - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1.0 - - - - - DocSetFeedName - SQLClient Documentation - - DocSetMinimumXcodeVersion - 3.0 - - DashDocSetFamily - appledoc - DocSetPublisherIdentifier - com.martinrybak.documentation - DocSetPublisherName - Martin Rybak - NSHumanReadableCopyright - Copyright © 2014 Martin Rybak. All rights reserved. - - diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/Classes/SQLClient.html b/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/Classes/SQLClient.html deleted file mode 100644 index cfac23d..0000000 --- a/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/Classes/SQLClient.html +++ /dev/null @@ -1,944 +0,0 @@ - - - - - SQLClient Class Reference - - - - - - -

- - - - -
- -
-
- - - -
- -
- - - - - - - -
Inherits fromNSObject
Declared inSQLClient.h
- - - - -
- -

Overview

-

Native SQL Server client for iOS. An Objective-C wrapper around the open-source FreeTDS library.

-
- - - - - -
- -

Tasks

- - - - - - - -
- - - - - -
- -

Properties

- -
- -

callbackQueue

- - - -
-

The queue for block callbacks. By default, uses the current queue upon singleton initialization. Can be overridden.

-
- - - -
@property (nonatomic, weak) NSOperationQueue *callbackQueue
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

charset

- - - -
-
    -
  • The character set to use for converting the UCS-2 server results. Default is UTF-8. -Can be overridden to any charset supported by the iconv library. -To list all supported iconv character sets, open a Terminal window and enter: -$ iconv –list
  • -
- -
- - - -
@property (nonatomic, copy) NSString *charset
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

database

- - - -
-

The database name to use

-
- - - -
@property (nonatomic, copy, readonly) NSString *database
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

delegate

- - - -
-

The delegate to receive error: and message: callbacks

-
- - - -
@property (nonatomic, weak) NSObject<SQLClientDelegate> *delegate
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

host

- - - -
-

The database server, i.e. server, server:port, or server\instance (be sure to escape the backslash)

-
- - - -
@property (nonatomic, copy, readonly) NSString *host
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

timeout

- - - -
-

Connection timeout, in seconds. Default is 5. Override before calling connect:

-
- - - -
@property (nonatomic, assign) int timeout
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

username

- - - -
-

The database username

-
- - - -
@property (nonatomic, copy, readonly) NSString *username
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

workerQueue

- - - -
-

The queue for database operations. By default, uses a new queue called ‘com.martinrybak.sqlclient’ created upon singleon intialization. Can be overridden.

-
- - - -
@property (nonatomic, strong) NSOperationQueue *workerQueue
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- - - -
- -

Class Methods

- -
- -

sharedInstance

- - - -
-

Returns an initialized SQLClient instance as a singleton

-
- - - -
+ (id)sharedInstance
- - - - - -
-

Return Value

-

Shared SQLClient object

-
- - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- - - -
- -

Instance Methods

- -
- -

connect:username:password:database:completion:

- - - -
-

Connects to a SQL database server

-
- - - -
- (void)connect:(NSString *)host username:(NSString *)username password:(NSString *)password database:(NSString *)database completion:(void ( ^ ) ( BOOL success ))completion
- - - -
-

Parameters

- -
-
host
-

Required. The database server, i.e. server, server:port, or server\instance (be sure to escape the backslash)

-
- -
-
username
-

Required. The database username

-
- -
-
password
-

Required. The database password

-
- -
-
database
-

Required. The database name

-
- -
-
completion
-

Block to be executed upon method successful connection

-
- -
-
delegate
-

Required. An NSObject that implements the SQLClientDelegate protocol for receiving error messages

-
- -
- - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

connected

- - - -
-

Indicates whether the database is currently connected

-
- - - -
- (BOOL)connected
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

disconnect

- - - -
-

Disconnects from database server

-
- - - -
- (void)disconnect
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

execute:completion:

- - - -
-

Executes a SQL statement. Results of queries will be passed to the completion handler. Inserts, updates, and deletes do not return results.

-
- - - -
- (void)execute:(NSString *)sql completion:(void ( ^ ) ( NSArray *results ))completion
- - - -
-

Parameters

- -
-
sql
-

Required. A SQL statement

-
- -
-
completion
-

Block to be executed upon method completion. Accepts an NSArray of tables. Each table is an NSArray of rows. Each row is an NSDictionary of columns where key = name and object = value as an NSString.

-
- -
- - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- - - - -
- - -
-
- - - \ No newline at end of file diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/Protocols/SQLClientDelegate.html b/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/Protocols/SQLClientDelegate.html deleted file mode 100644 index 802ee87..0000000 --- a/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/Protocols/SQLClientDelegate.html +++ /dev/null @@ -1,369 +0,0 @@ - - - - - SQLClientDelegate Protocol Reference - - - - - - -
- - - - -
- -
-
- - - -
- -
- - - - - - - -
Conforms toNSObject
Declared inSQLClient.h
- - - - - - -
- -

Tasks

- - - - - - - -
- - - - - - - - - -
- -

Instance Methods

- -
- -

error:code:severity:

- - - -
-

Required delegate method to receive error notifications

-
- - - -
- (void)error:(NSString *)error code:(int)code severity:(int)severity
- - - -
-

Parameters

- -
-
error
-

Error text

-
- -
-
code
-

FreeTDS error code

-
- -
-
severity
-

FreeTDS error severity

-
- -
- - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

message:

- - - -
-

Optional delegate method to receive message notifications

-
- - - -
- (void)message:(NSString *)message
- - - -
-

Parameters

- -
-
message
-

Message text

-
- -
- - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- - - - -
- - -
-
- - - \ No newline at end of file diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/css/styles.css b/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/css/styles.css deleted file mode 100644 index 3308189..0000000 --- a/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/css/styles.css +++ /dev/null @@ -1,615 +0,0 @@ -body { - font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; - font-size: 13px; -} - -code { - font-family: Courier, Consolas, monospace; - font-size: 13px; - color: #666; -} - -pre { - font-family: Courier, Consolas, monospace; - font-size: 13px; - line-height: 18px; - tab-interval: 0.5em; - border: 1px solid #C7CFD5; - background-color: #F1F5F9; - color: #666; - padding: 0.3em 1em; -} - -ul { - list-style-type: square; -} - -li { - margin-bottom: 10px; -} - -a, a code { - text-decoration: none; - color: #36C; -} - -a:hover, a:hover code { - text-decoration: underline; - color: #36C; -} - -h2 { - border-bottom: 1px solid #8391A8; - color: #3C4C6C; - font-size: 187%; - font-weight: normal; - margin-top: 1.75em; - padding-bottom: 2px; -} - -table { - margin-bottom: 4em; - border-collapse:collapse; - vertical-align: middle; -} - -td { - border: 1px solid #9BB3CD; - padding: .667em; - font-size: 100%; -} - -th { - border: 1px solid #9BB3CD; - padding: .3em .667em .3em .667em; - background: #93A5BB; - font-size: 103%; - font-weight: bold; - color: white; - text-align: left; -} - -/* @group Common page elements */ - -#top_header { - height: 91px; - left: 0; - min-width: 598px; - position: absolute; - right: 0; - top: 0; - z-index: 900; -} - -#footer { - clear: both; - padding-top: 20px; - text-align: center; -} - -#contents, #overview_contents { - -webkit-overflow-scrolling: touch; - border-top: 1px solid #A9A9A9; - position: absolute; - top: 90px; - left: 0; - right: 0; - bottom: 0; - overflow-x: hidden; - overflow-y: auto; - padding-left: 2em; - padding-right: 2em; - padding-top: 1em; - min-width: 550px; -} - -#contents.isShowingTOC { - left: 230px; - min-width: 320px; -} - -.copyright { - font-size: 12px; -} - -.generator { - font-size: 11px; -} - -.main-navigation ul li { - display: inline; - margin-left: 15px; - list-style: none; -} - -.navigation-top { - clear: both; - float: right; -} - -.navigation-bottom { - clear: both; - float: right; - margin-top: 20px; - margin-bottom: -10px; -} - -.open > .disclosure { - background-image: url("../img/disclosure_open.png"); -} - -.disclosure { - background: url("../img/disclosure.png") no-repeat scroll 0 0; -} - -.disclosure, .nodisclosure { - display: inline-block; - height: 8px; - margin-right: 5px; - position: relative; - width: 9px; -} - -/* @end */ - -/* @group Header */ - -#top_header #library { - background: url("../img/library_background.png") repeat-x 0 0 #485E78; - background-color: #ccc; - height: 35px; - font-size: 115%; -} - -#top_header #library #libraryTitle { - color: #FFFFFF; - margin-left: 15px; - text-shadow: 0 -1px 0 #485E78; - top: 8px; - position: absolute; -} - -#libraryTitle { - left: 0; -} - -#top_header #library #developerHome { - color: #92979E; - right: 15px; - top: 8px; - position: absolute; -} - -#top_header #library a:hover { - text-decoration: none; -} - -#top_header #title { - background: url("../img/title_background.png") repeat-x 0 0 #8A98A9; - border-bottom: 1px solid #757575; - height: 25px; - overflow: hidden; -} - -#top_header h1 { - font-size: 105%; - font-weight: normal; - margin: 0; - padding: 3px 0 2px; - text-align: center; - /*text-shadow: 0 1px 0 #D5D5D5;*/ - white-space: nowrap; -} - -#headerButtons { - background-color: #D8D8D8; - background-image: url("../img/button_bar_background.png"); - border-bottom: 0px solid #EDEDED; - border-top: 0px solid #a8a8a8; - font-size: 8pt; - height: 28px; - left: 0; - list-style: none outside none; - margin: 0; - overflow: hidden; - padding: 0; - position: absolute; - right: 0; - top: 61px; -} - -#headerButtons li { - background-repeat: no-repeat; - display: inline; - margin-top: 0; - margin-bottom: 0; - padding: 0; -} - -#toc_button button { - background-color: #EBEEF1; - border-color: #ACACAC; - border-style: none solid none none; - border-width: 0 1px 0 0; - height: 28px; - margin: 0; - padding-left: 30px; - text-align: left; - width: 230px; -} - -li#jumpto_button { - left: 230px; - margin-left: 0; - position: absolute; -} - -li#jumpto_button select { - height: 22px; - margin: 5px 2px 0 10px; - max-width: 300px; -} - -/* @end */ - -/* @group Table of contents */ - -#tocContainer.isShowingTOC { - border-right: 1px solid #ACACAC; - display: block; - overflow-x: hidden; - overflow-y: auto; - padding: 0; -} - -#tocContainer { - background-color: #EBEEF1; - border-top: 1px solid #ACACAC; - bottom: 0; - display: none; - left: 0; - overflow: hidden; - position: absolute; - top: 90px; - width: 229px; -} - -#tocContainer > ul#toc { - font-size: 11px; - margin: 0; - padding: 12px 0 18px; - width: 209px; - -moz-user-select: none; - -webkit-user-select: none; - user-select: none; -} - -#tocContainer > ul#toc > li { - margin: 0; - padding: 0 0 7px 30px; - text-indent: -15px; -} - -#tocContainer > ul#toc > li > .sectionName a { - color: #000000; - font-weight: bold; -} - -#tocContainer > ul#toc > li > .sectionName a:hover { - text-decoration: none; -} - -#tocContainer > ul#toc li.children > ul { - display: none; - height: 0; -} - -#tocContainer > ul#toc > li > ul { - margin: 0; - padding: 0; -} - -#tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { - margin-left: 0; - margin-bottom: 0; - padding-left: 15px; -} - -#tocContainer > ul#toc > li ul { - list-style: none; - margin-right: 0; - padding-right: 0; -} - -#tocContainer > ul#toc li.children.open > ul { - display: block; - height: auto; - margin-left: -15px; - padding-left: 0; -} - -#tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { - margin-left: 0; - padding-left: 15px; -} - -#tocContainer li ul li { - margin-top: 0.583em; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -#tocContainer li ul li span.sectionName { - white-space: normal; -} - -#tocContainer > ul#toc > li > ul > li > .sectionName a { - font-weight: bold; -} - -#tocContainer > ul#toc > li > ul a { - color: #4F4F4F; -} - -/* @end */ - -/* @group Index formatting */ - -.index-title { - font-size: 13px; - font-weight: normal; -} - -.index-column { - float: left; - width: 30%; - min-width: 200px; - font-size: 11px; -} - -.index-column ul { - margin: 8px 0 0 0; - padding: 0; - list-style: none; -} - -.index-column ul li { - margin: 0 0 3px 0; - padding: 0; -} - -.hierarchy-column { - min-width: 400px; -} - -.hierarchy-column ul { - margin: 3px 0 0 15px; -} - -.hierarchy-column ul li { - list-style-type: square; -} - -/* @end */ - -/* @group Common formatting elements */ - -.title { - font-weight: normal; - font-size: 215%; - margin-top:0; -} - -.subtitle { - font-weight: normal; - font-size: 180%; - color: #3C4C6C; - border-bottom: 1px solid #5088C5; -} - -.subsubtitle { - font-weight: normal; - font-size: 145%; - height: 0.7em; -} - -.note { - border: 1px solid #5088C5; - background-color: white; - margin: 1.667em 0 1.75em 0; - padding: 0 .667em .083em .750em; -} - -.warning { - border: 1px solid #5088C5; - background-color: #F0F3F7; - margin-bottom: 0.5em; - padding: 0.3em 0.8em; -} - -.bug { - border: 1px solid #000; - background-color: #ffffcc; - margin-bottom: 0.5em; - padding: 0.3em 0.8em; -} - -.deprecated { - color: #F60425; -} - -/* @end */ - -/* @group Common layout */ - -.section { - margin-top: 3em; -} - -/* @end */ - -/* @group Object specification section */ - -.section-specification { - margin-left: 2.5em; - margin-right: 2.5em; - font-size: 12px; -} - -.section-specification table { - margin-bottom: 0em; - border-top: 1px solid #d6e0e5; -} - -.section-specification td { - vertical-align: top; - border-bottom: 1px solid #d6e0e5; - border-left-width: 0px; - border-right-width: 0px; - border-top-width: 0px; - padding: .6em; -} - -.section-specification .specification-title { - font-weight: bold; -} - -/* @end */ - -/* @group Tasks section */ - -.task-list { - list-style-type: none; - padding-left: 0px; -} - -.task-list li { - margin-bottom: 3px; -} - -.task-item-suffix { - color: #996; - font-size: 12px; - font-style: italic; - margin-left: 0.5em; -} - -span.tooltip span.tooltip { - font-size: 1.0em; - display: none; - padding: 0.3em; - border: 1px solid #aaa; - background-color: #fdfec8; - color: #000; - text-align: left; -} - -span.tooltip:hover span.tooltip { - display: block; - position: absolute; - margin-left: 2em; -} - -/* @end */ - -/* @group Method section */ - -.section-method { - margin-top: 2.3em; -} - -.method-title { - margin-bottom: 1.5em; -} - -.method-subtitle { - margin-top: 0.7em; - margin-bottom: 0.2em; -} - -.method-subsection p { - margin-top: 0.4em; - margin-bottom: 0.8em; -} - -.method-declaration { - margin-top:1.182em; - margin-bottom:.909em; -} - -.method-declaration code { - font:14px Courier, Consolas, monospace; - color:#000; -} - -.declaration { - color: #000; -} - -.termdef { - margin-bottom: 10px; - margin-left: 0px; - margin-right: 0px; - margin-top: 0px; -} - -.termdef dt { - margin: 0; - padding: 0; -} - -.termdef dd { - margin-bottom: 6px; - margin-left: 16px; - margin-right: 0px; - margin-top: 1px; -} - -.termdef dd p { - margin-bottom: 6px; - margin-left: 0px; - margin-right: 0px; - margin-top: -1px; -} - -.argument-def { - margin-top: 0.3em; - margin-bottom: 0.3em; -} - -.argument-def dd { - margin-left: 1.25em; -} - -.see-also-section ul { - list-style-type: none; - padding-left: 0px; - margin-top: 0; -} - -.see-also-section li { - margin-bottom: 3px; -} - -.declared-in-ref { - color: #666; -} - -#tocContainer.hideInXcode { - display: none; - border: 0px solid black; -} - -#top_header.hideInXcode { - display: none; -} - -#contents.hideInXcode { - border: 0px solid black; - top: 0px; - left: 0px; -} - -/* @end */ - diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/css/stylesPrint.css b/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/css/stylesPrint.css deleted file mode 100644 index dc54cd2..0000000 --- a/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/css/stylesPrint.css +++ /dev/null @@ -1,22 +0,0 @@ - -header { - display: none; -} - -div.main-navigation, div.navigation-top { - display: none; -} - -div#overview_contents, div#contents.isShowingTOC, div#contents { - overflow: visible; - position: relative; - top: 0px; - border: none; - left: 0; -} -#tocContainer.isShowingTOC { - display: none; -} -nav { - display: none; -} \ No newline at end of file diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/hierarchy.html b/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/hierarchy.html deleted file mode 100644 index 5b6a6d3..0000000 --- a/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/hierarchy.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - SQLClient Hierarchy - - - - - -
- - - - -
-
-
- - -
- -
-

Class Hierarchy

- - - -
- - - -
- -

Protocol References

- - - - -
- -
- - -
-
- - \ No newline at end of file diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/img/button_bar_background.png b/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/img/button_bar_background.png deleted file mode 100644 index 71d1019bc0c5c571dea84ff9775aa243444d2d4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2825 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 b18@fb!nF!3r##!100000NkvXXu0mjf;Lkd# diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/img/disclosure.png b/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/img/disclosure.png deleted file mode 100644 index 4c5cbf445602efefe8ee0475703d36ba4132ff21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115 zcmeAS@N?(olHy`uVBq!ia0vp^oIuRM!2%>3kI32qDJxGG#}JO_`KyqCDY z*KXE?%k|6ufBPK!<6_I4f7W}-SGbGsdN0tg!V=WD=wjWz$u?adUkEVRH#$H4{$dk9 P&=dwwS3j3^P63kI32qDKAeK#}JO|$v@6N$mnnQFZs`y zVdCMs<^KgT?B%tdsPGFGRJ=dLFZ%0zg1x*>$p86_dQT3$|8Vc2oeOUWM@HOU=Rfsq fB8n*;t{e<2Cp(|jRn^)AG?l^A)z4*}Q$iB}AA>A0 diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/img/library_background.png b/SQLClient/SQLClientDocs/docset/Contents/Resources/Documents/img/library_background.png deleted file mode 100644 index 3006248afe8822e2c9761d9e76a900b2e9781366..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!3HEfN&F!HD`A7 zGAIf#2eRk&s%13)vlW+lq|A78-&7mDt%)5zO3YC!PX6C6d@N^ihs=xXE6;3R6D_$Y ZpF#6xy3}8Re4y=ES4z)+>iz|hdl!0_`wkbcR)P-?)y@G60U!DwdS5Kjv*DdV!92v z7!-Jp7=8I~ult4BS5`!1y1 - - - - SQLClient Reference - - - - - -
- - - - -
-
-
- - -
- - - - - -
-

Class References

- -
- - - -
- -

Protocol References

- - - - -
- -
- - -
-
- - \ No newline at end of file diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/Nodes.xml b/SQLClient/SQLClientDocs/docset/Contents/Resources/Nodes.xml deleted file mode 100644 index 96c6fd6..0000000 --- a/SQLClient/SQLClientDocs/docset/Contents/Resources/Nodes.xml +++ /dev/null @@ -1,107 +0,0 @@ - - - - - SQLClient - index.html - - - - - Classes - index.html - - - - - - - - - - Protocols - index.html - - - - - - - - - - - - - - SQLClient - Classes/SQLClient.html - - - - Classes/SQLClient.html - Overview - overview - - - Classes/SQLClient.html - Tasks - tasks - - - - Classes/SQLClient.html - Properties - properties - - - - - Classes/SQLClient.html - Class Methods - class_methods - - - - - Classes/SQLClient.html - Instance Methods - instance_methods - - - - - - - - - SQLClientDelegate - Protocols/SQLClientDelegate.html - - - - Protocols/SQLClientDelegate.html - Overview - overview - - - Protocols/SQLClientDelegate.html - Tasks - tasks - - - - - - Protocols/SQLClientDelegate.html - Instance Methods - instance_methods - - - - - - - - - \ No newline at end of file diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/Tokens1.xml b/SQLClient/SQLClientDocs/docset/Contents/Resources/Tokens1.xml deleted file mode 100644 index 5e2f022..0000000 --- a/SQLClient/SQLClientDocs/docset/Contents/Resources/Tokens1.xml +++ /dev/null @@ -1,402 +0,0 @@ - - - - - - //apple_ref/occ/cl/SQLClient - Native SQL Server client for iOS. An Objective-C wrapper around the open-source FreeTDS library. - SQLClient.h - - - - - - - - //apple_ref/occ/instm/SQLClient/setTimeout: - Connection timeout, in seconds. Default is 5. Override before calling connect: - SQLClient.h - - @property (nonatomic, assign) int timeout - - - //api/name/timeout - - - - - //apple_ref/occ/instm/SQLClient/timeout - Connection timeout, in seconds. Default is 5. Override before calling connect: - SQLClient.h - - @property (nonatomic, assign) int timeout - - - //api/name/timeout - - - - - //apple_ref/occ/instp/SQLClient/timeout - Connection timeout, in seconds. Default is 5. Override before calling connect: - SQLClient.h - - @property (nonatomic, assign) int timeout - - - //api/name/timeout - - - - - //apple_ref/occ/instm/SQLClient/setHost: - The database server, i.e. server, server:port, or server\instance (be sure to escape the backslash) - SQLClient.h - - @property (nonatomic, copy, readonly) NSString *host - - - //api/name/host - - - - - //apple_ref/occ/instm/SQLClient/host - The database server, i.e. server, server:port, or server\instance (be sure to escape the backslash) - SQLClient.h - - @property (nonatomic, copy, readonly) NSString *host - - - //api/name/host - - - - - //apple_ref/occ/instp/SQLClient/host - The database server, i.e. server, server:port, or server\instance (be sure to escape the backslash) - SQLClient.h - - @property (nonatomic, copy, readonly) NSString *host - - - //api/name/host - - - - - //apple_ref/occ/instm/SQLClient/setUsername: - The database username - SQLClient.h - - @property (nonatomic, copy, readonly) NSString *username - - - //api/name/username - - - - - //apple_ref/occ/instm/SQLClient/username - The database username - SQLClient.h - - @property (nonatomic, copy, readonly) NSString *username - - - //api/name/username - - - - - //apple_ref/occ/instp/SQLClient/username - The database username - SQLClient.h - - @property (nonatomic, copy, readonly) NSString *username - - - //api/name/username - - - - - //apple_ref/occ/instm/SQLClient/setDatabase: - The database name to use - SQLClient.h - - @property (nonatomic, copy, readonly) NSString *database - - - //api/name/database - - - - - //apple_ref/occ/instm/SQLClient/database - The database name to use - SQLClient.h - - @property (nonatomic, copy, readonly) NSString *database - - - //api/name/database - - - - - //apple_ref/occ/instp/SQLClient/database - The database name to use - SQLClient.h - - @property (nonatomic, copy, readonly) NSString *database - - - //api/name/database - - - - - //apple_ref/occ/instm/SQLClient/setDelegate: - The delegate to receive error: and message: callbacks - SQLClient.h - - @property (nonatomic, weak) NSObject<SQLClientDelegate> *delegate - - - //api/name/delegate - - - - - //apple_ref/occ/instm/SQLClient/delegate - The delegate to receive error: and message: callbacks - SQLClient.h - - @property (nonatomic, weak) NSObject<SQLClientDelegate> *delegate - - - //api/name/delegate - - - - - //apple_ref/occ/instp/SQLClient/delegate - The delegate to receive error: and message: callbacks - SQLClient.h - - @property (nonatomic, weak) NSObject<SQLClientDelegate> *delegate - - - //api/name/delegate - - - - - //apple_ref/occ/instm/SQLClient/setWorkerQueue: - The queue for database operations. By default, uses a new queue called 'com.martinrybak.sqlclient' created upon singleon intialization. Can be overridden. - SQLClient.h - - @property (nonatomic, strong) NSOperationQueue *workerQueue - - - //api/name/workerQueue - - - - - //apple_ref/occ/instm/SQLClient/workerQueue - The queue for database operations. By default, uses a new queue called 'com.martinrybak.sqlclient' created upon singleon intialization. Can be overridden. - SQLClient.h - - @property (nonatomic, strong) NSOperationQueue *workerQueue - - - //api/name/workerQueue - - - - - //apple_ref/occ/instp/SQLClient/workerQueue - The queue for database operations. By default, uses a new queue called 'com.martinrybak.sqlclient' created upon singleon intialization. Can be overridden. - SQLClient.h - - @property (nonatomic, strong) NSOperationQueue *workerQueue - - - //api/name/workerQueue - - - - - //apple_ref/occ/instm/SQLClient/setCallbackQueue: - The queue for block callbacks. By default, uses the current queue upon singleton initialization. Can be overridden. - SQLClient.h - - @property (nonatomic, weak) NSOperationQueue *callbackQueue - - - //api/name/callbackQueue - - - - - //apple_ref/occ/instm/SQLClient/callbackQueue - The queue for block callbacks. By default, uses the current queue upon singleton initialization. Can be overridden. - SQLClient.h - - @property (nonatomic, weak) NSOperationQueue *callbackQueue - - - //api/name/callbackQueue - - - - - //apple_ref/occ/instp/SQLClient/callbackQueue - The queue for block callbacks. By default, uses the current queue upon singleton initialization. Can be overridden. - SQLClient.h - - @property (nonatomic, weak) NSOperationQueue *callbackQueue - - - //api/name/callbackQueue - - - - - //apple_ref/occ/instm/SQLClient/setCharset: - * The character set to use for converting the UCS-2 server results. Default is UTF-8. -Can be overridden to any charset supported by the iconv library. -To list all supported iconv character sets, open a Terminal window and enter: -$ iconv --list - SQLClient.h - - @property (nonatomic, copy) NSString *charset - - - //api/name/charset - - - - - //apple_ref/occ/instm/SQLClient/charset - * The character set to use for converting the UCS-2 server results. Default is UTF-8. -Can be overridden to any charset supported by the iconv library. -To list all supported iconv character sets, open a Terminal window and enter: -$ iconv --list - SQLClient.h - - @property (nonatomic, copy) NSString *charset - - - //api/name/charset - - - - - //apple_ref/occ/instp/SQLClient/charset - * The character set to use for converting the UCS-2 server results. Default is UTF-8. -Can be overridden to any charset supported by the iconv library. -To list all supported iconv character sets, open a Terminal window and enter: -$ iconv --list - SQLClient.h - - @property (nonatomic, copy) NSString *charset - - - //api/name/charset - - - - - //apple_ref/occ/clm/SQLClient/sharedInstance - Returns an initialized SQLClient instance as a singleton - SQLClient.h - - + (id)sharedInstance - - Shared SQLClient object - //api/name/sharedInstance - - - - - //apple_ref/occ/instm/SQLClient/connect:username:password:database:completion: - Connects to a SQL database server - SQLClient.h - - - (void)connect:(NSString *)host username:(NSString *)username password:(NSString *)password database:(NSString *)database completion:(void ( ^ ) ( BOOL success ))completion - - - host - Required. The database server, i.e. server, server:port, or server\instance (be sure to escape the backslash) - - username - Required. The database username - - password - Required. The database password - - database - Required. The database name - - completion - Block to be executed upon method successful connection - - delegate - Required. An NSObject that implements the SQLClientDelegate protocol for receiving error messages - - - - //api/name/connect:username:password:database:completion: - - - - - //apple_ref/occ/instm/SQLClient/connected - Indicates whether the database is currently connected - SQLClient.h - - - (BOOL)connected - - - //api/name/connected - - - - - //apple_ref/occ/instm/SQLClient/execute:completion: - Executes a SQL statement. Results of queries will be passed to the completion handler. Inserts, updates, and deletes do not return results. - SQLClient.h - - - (void)execute:(NSString *)sql completion:(void ( ^ ) ( NSArray *results ))completion - - - sql - Required. A SQL statement - - completion - Block to be executed upon method completion. Accepts an NSArray of tables. Each table is an NSArray of rows. Each row is an NSDictionary of columns where key = name and object = value as an NSString. - - - - //api/name/execute:completion: - - - - - //apple_ref/occ/instm/SQLClient/disconnect - Disconnects from database server - SQLClient.h - - - (void)disconnect - - - //api/name/disconnect - - - - - - \ No newline at end of file diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/Tokens2.xml b/SQLClient/SQLClientDocs/docset/Contents/Resources/Tokens2.xml deleted file mode 100644 index 4854111..0000000 --- a/SQLClient/SQLClientDocs/docset/Contents/Resources/Tokens2.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - //apple_ref/occ/intf/SQLClientDelegate - - SQLClient.h - - - - - - - - //apple_ref/occ/intfm/SQLClientDelegate/error:code:severity: - Required delegate method to receive error notifications - SQLClient.h - - - (void)error:(NSString *)error code:(int)code severity:(int)severity - - - error - Error text - - code - FreeTDS error code - - severity - FreeTDS error severity - - - - //api/name/error:code:severity: - - - - - //apple_ref/occ/intfm/SQLClientDelegate/message: - Optional delegate method to receive message notifications - SQLClient.h - - - (void)message:(NSString *)message - - - message - Message text - - - - //api/name/message: - - - - - - \ No newline at end of file diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/docSet.dsidx b/SQLClient/SQLClientDocs/docset/Contents/Resources/docSet.dsidx deleted file mode 100644 index 67dc0ebd42038218f46bb4cbc18623165491bd04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217088 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 K|4rZirT-5R{;rw; diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/docSet.skidx b/SQLClient/SQLClientDocs/docset/Contents/Resources/docSet.skidx deleted file mode 100644 index 962de0f4358477382bfa7d2242598c55ec12080c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13312 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 K4%8v`75@W&#HUCA diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/docSet.toc b/SQLClient/SQLClientDocs/docset/Contents/Resources/docSet.toc deleted file mode 100644 index 0cd9010a18318a9e5a824e8194f51c495903a474..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 770 zcmZ9KOK1~O6o${ac~4B7rfF=|sI`rcYTKl~YK-;KXcHSB0ZUWy!8mhm2PZRW?pWQ3 zb<>@Q3&oW%N)e&rN^~cPyY6feUAb@}xD#>VO7DbTi5|F|%lZEE-^1Zvt{FPB)9Lp} zJclc$rO~2!#X7H>8ofcav$S4i&%LYS?dvynZ|}|Q*?(}!=MMx!;j&(?mF?EwJZ1s> zjwIu0Sw?_a0stPKz5Fcu{E`VPg^i0l z)~*vc$Z_C5oZPTcV6PJ^1vw7!Bg{KDcM0s_-7Xg9jTuv0wr&#w;>eDacgJ_%?b)$Y z03SDY-)%PT>rV?Da3sD0i3^%n;Cc+~+ATzq6KfI)r^XDiz5528=#ym6K;fUdls%Ah zQuCc1ZNa{Rn0m<<3CKf-9;c#>QkXA&;mFb9{ITOBh2n|P(wQ;UP#7DpQhUnM*aVw4 zR70nxJ>IBOHc_ivwxwDIJI8M|TDNt|#%zUMWfg0N)@O8j{gT}Bw@;%6om6aE(M@J6 zrb;hpmU^wq9Ti)*Ob9{*63_)17=$e3UJMaJ=!aOX% zOLztEU=fz#1AKvR@B@A#!7#=#f$f;YZtTT=%;7MW@DwUIh11Az4)5YUypNCYDL%s& r_!{5fTU^AC_z6GbSNx8@h=)W;oFqwxWcg-z5dJkAadIWEBvSYT(8JA= diff --git a/SQLClient/SQLClientDocs/docset/Contents/Resources/docSet.tokencache b/SQLClient/SQLClientDocs/docset/Contents/Resources/docSet.tokencache deleted file mode 100644 index b463d904c724e062001d69ac34ee0b63635e868e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4919 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 E3#WHy1poj5 diff --git a/SQLClient/SQLClientDocs/html/Classes/SQLClient.html b/SQLClient/SQLClientDocs/html/Classes/SQLClient.html deleted file mode 100644 index cfac23d..0000000 --- a/SQLClient/SQLClientDocs/html/Classes/SQLClient.html +++ /dev/null @@ -1,944 +0,0 @@ - - - - - SQLClient Class Reference - - - - - - -
- - - - -
- -
-
- - - -
- -
- - - - - - - -
Inherits fromNSObject
Declared inSQLClient.h
- - - - -
- -

Overview

-

Native SQL Server client for iOS. An Objective-C wrapper around the open-source FreeTDS library.

-
- - - - - -
- -

Tasks

- - - - - - - -
- - - - - -
- -

Properties

- -
- -

callbackQueue

- - - -
-

The queue for block callbacks. By default, uses the current queue upon singleton initialization. Can be overridden.

-
- - - -
@property (nonatomic, weak) NSOperationQueue *callbackQueue
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

charset

- - - -
-
    -
  • The character set to use for converting the UCS-2 server results. Default is UTF-8. -Can be overridden to any charset supported by the iconv library. -To list all supported iconv character sets, open a Terminal window and enter: -$ iconv –list
  • -
- -
- - - -
@property (nonatomic, copy) NSString *charset
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

database

- - - -
-

The database name to use

-
- - - -
@property (nonatomic, copy, readonly) NSString *database
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

delegate

- - - -
-

The delegate to receive error: and message: callbacks

-
- - - -
@property (nonatomic, weak) NSObject<SQLClientDelegate> *delegate
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

host

- - - -
-

The database server, i.e. server, server:port, or server\instance (be sure to escape the backslash)

-
- - - -
@property (nonatomic, copy, readonly) NSString *host
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

timeout

- - - -
-

Connection timeout, in seconds. Default is 5. Override before calling connect:

-
- - - -
@property (nonatomic, assign) int timeout
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

username

- - - -
-

The database username

-
- - - -
@property (nonatomic, copy, readonly) NSString *username
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

workerQueue

- - - -
-

The queue for database operations. By default, uses a new queue called ‘com.martinrybak.sqlclient’ created upon singleon intialization. Can be overridden.

-
- - - -
@property (nonatomic, strong) NSOperationQueue *workerQueue
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- - - -
- -

Class Methods

- -
- -

sharedInstance

- - - -
-

Returns an initialized SQLClient instance as a singleton

-
- - - -
+ (id)sharedInstance
- - - - - -
-

Return Value

-

Shared SQLClient object

-
- - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- - - -
- -

Instance Methods

- -
- -

connect:username:password:database:completion:

- - - -
-

Connects to a SQL database server

-
- - - -
- (void)connect:(NSString *)host username:(NSString *)username password:(NSString *)password database:(NSString *)database completion:(void ( ^ ) ( BOOL success ))completion
- - - -
-

Parameters

- -
-
host
-

Required. The database server, i.e. server, server:port, or server\instance (be sure to escape the backslash)

-
- -
-
username
-

Required. The database username

-
- -
-
password
-

Required. The database password

-
- -
-
database
-

Required. The database name

-
- -
-
completion
-

Block to be executed upon method successful connection

-
- -
-
delegate
-

Required. An NSObject that implements the SQLClientDelegate protocol for receiving error messages

-
- -
- - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

connected

- - - -
-

Indicates whether the database is currently connected

-
- - - -
- (BOOL)connected
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

disconnect

- - - -
-

Disconnects from database server

-
- - - -
- (void)disconnect
- - - - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

execute:completion:

- - - -
-

Executes a SQL statement. Results of queries will be passed to the completion handler. Inserts, updates, and deletes do not return results.

-
- - - -
- (void)execute:(NSString *)sql completion:(void ( ^ ) ( NSArray *results ))completion
- - - -
-

Parameters

- -
-
sql
-

Required. A SQL statement

-
- -
-
completion
-

Block to be executed upon method completion. Accepts an NSArray of tables. Each table is an NSArray of rows. Each row is an NSDictionary of columns where key = name and object = value as an NSString.

-
- -
- - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- - - - -
- - -
-
- - - \ No newline at end of file diff --git a/SQLClient/SQLClientDocs/html/Protocols/SQLClientDelegate.html b/SQLClient/SQLClientDocs/html/Protocols/SQLClientDelegate.html deleted file mode 100644 index 802ee87..0000000 --- a/SQLClient/SQLClientDocs/html/Protocols/SQLClientDelegate.html +++ /dev/null @@ -1,369 +0,0 @@ - - - - - SQLClientDelegate Protocol Reference - - - - - - -
- - - - -
- -
-
- - - -
- -
- - - - - - - -
Conforms toNSObject
Declared inSQLClient.h
- - - - - - -
- -

Tasks

- - - - - - - -
- - - - - - - - - -
- -

Instance Methods

- -
- -

error:code:severity:

- - - -
-

Required delegate method to receive error notifications

-
- - - -
- (void)error:(NSString *)error code:(int)code severity:(int)severity
- - - -
-

Parameters

- -
-
error
-

Error text

-
- -
-
code
-

FreeTDS error code

-
- -
-
severity
-

FreeTDS error severity

-
- -
- - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- -

message:

- - - -
-

Optional delegate method to receive message notifications

-
- - - -
- (void)message:(NSString *)message
- - - -
-

Parameters

- -
-
message
-

Message text

-
- -
- - - - - - - - - - - - - -
-

Declared In

- SQLClient.h
-
- - -
- -
- - - - -
- - -
-
- - - \ No newline at end of file diff --git a/SQLClient/SQLClientDocs/html/css/styles.css b/SQLClient/SQLClientDocs/html/css/styles.css deleted file mode 100644 index 3308189..0000000 --- a/SQLClient/SQLClientDocs/html/css/styles.css +++ /dev/null @@ -1,615 +0,0 @@ -body { - font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; - font-size: 13px; -} - -code { - font-family: Courier, Consolas, monospace; - font-size: 13px; - color: #666; -} - -pre { - font-family: Courier, Consolas, monospace; - font-size: 13px; - line-height: 18px; - tab-interval: 0.5em; - border: 1px solid #C7CFD5; - background-color: #F1F5F9; - color: #666; - padding: 0.3em 1em; -} - -ul { - list-style-type: square; -} - -li { - margin-bottom: 10px; -} - -a, a code { - text-decoration: none; - color: #36C; -} - -a:hover, a:hover code { - text-decoration: underline; - color: #36C; -} - -h2 { - border-bottom: 1px solid #8391A8; - color: #3C4C6C; - font-size: 187%; - font-weight: normal; - margin-top: 1.75em; - padding-bottom: 2px; -} - -table { - margin-bottom: 4em; - border-collapse:collapse; - vertical-align: middle; -} - -td { - border: 1px solid #9BB3CD; - padding: .667em; - font-size: 100%; -} - -th { - border: 1px solid #9BB3CD; - padding: .3em .667em .3em .667em; - background: #93A5BB; - font-size: 103%; - font-weight: bold; - color: white; - text-align: left; -} - -/* @group Common page elements */ - -#top_header { - height: 91px; - left: 0; - min-width: 598px; - position: absolute; - right: 0; - top: 0; - z-index: 900; -} - -#footer { - clear: both; - padding-top: 20px; - text-align: center; -} - -#contents, #overview_contents { - -webkit-overflow-scrolling: touch; - border-top: 1px solid #A9A9A9; - position: absolute; - top: 90px; - left: 0; - right: 0; - bottom: 0; - overflow-x: hidden; - overflow-y: auto; - padding-left: 2em; - padding-right: 2em; - padding-top: 1em; - min-width: 550px; -} - -#contents.isShowingTOC { - left: 230px; - min-width: 320px; -} - -.copyright { - font-size: 12px; -} - -.generator { - font-size: 11px; -} - -.main-navigation ul li { - display: inline; - margin-left: 15px; - list-style: none; -} - -.navigation-top { - clear: both; - float: right; -} - -.navigation-bottom { - clear: both; - float: right; - margin-top: 20px; - margin-bottom: -10px; -} - -.open > .disclosure { - background-image: url("../img/disclosure_open.png"); -} - -.disclosure { - background: url("../img/disclosure.png") no-repeat scroll 0 0; -} - -.disclosure, .nodisclosure { - display: inline-block; - height: 8px; - margin-right: 5px; - position: relative; - width: 9px; -} - -/* @end */ - -/* @group Header */ - -#top_header #library { - background: url("../img/library_background.png") repeat-x 0 0 #485E78; - background-color: #ccc; - height: 35px; - font-size: 115%; -} - -#top_header #library #libraryTitle { - color: #FFFFFF; - margin-left: 15px; - text-shadow: 0 -1px 0 #485E78; - top: 8px; - position: absolute; -} - -#libraryTitle { - left: 0; -} - -#top_header #library #developerHome { - color: #92979E; - right: 15px; - top: 8px; - position: absolute; -} - -#top_header #library a:hover { - text-decoration: none; -} - -#top_header #title { - background: url("../img/title_background.png") repeat-x 0 0 #8A98A9; - border-bottom: 1px solid #757575; - height: 25px; - overflow: hidden; -} - -#top_header h1 { - font-size: 105%; - font-weight: normal; - margin: 0; - padding: 3px 0 2px; - text-align: center; - /*text-shadow: 0 1px 0 #D5D5D5;*/ - white-space: nowrap; -} - -#headerButtons { - background-color: #D8D8D8; - background-image: url("../img/button_bar_background.png"); - border-bottom: 0px solid #EDEDED; - border-top: 0px solid #a8a8a8; - font-size: 8pt; - height: 28px; - left: 0; - list-style: none outside none; - margin: 0; - overflow: hidden; - padding: 0; - position: absolute; - right: 0; - top: 61px; -} - -#headerButtons li { - background-repeat: no-repeat; - display: inline; - margin-top: 0; - margin-bottom: 0; - padding: 0; -} - -#toc_button button { - background-color: #EBEEF1; - border-color: #ACACAC; - border-style: none solid none none; - border-width: 0 1px 0 0; - height: 28px; - margin: 0; - padding-left: 30px; - text-align: left; - width: 230px; -} - -li#jumpto_button { - left: 230px; - margin-left: 0; - position: absolute; -} - -li#jumpto_button select { - height: 22px; - margin: 5px 2px 0 10px; - max-width: 300px; -} - -/* @end */ - -/* @group Table of contents */ - -#tocContainer.isShowingTOC { - border-right: 1px solid #ACACAC; - display: block; - overflow-x: hidden; - overflow-y: auto; - padding: 0; -} - -#tocContainer { - background-color: #EBEEF1; - border-top: 1px solid #ACACAC; - bottom: 0; - display: none; - left: 0; - overflow: hidden; - position: absolute; - top: 90px; - width: 229px; -} - -#tocContainer > ul#toc { - font-size: 11px; - margin: 0; - padding: 12px 0 18px; - width: 209px; - -moz-user-select: none; - -webkit-user-select: none; - user-select: none; -} - -#tocContainer > ul#toc > li { - margin: 0; - padding: 0 0 7px 30px; - text-indent: -15px; -} - -#tocContainer > ul#toc > li > .sectionName a { - color: #000000; - font-weight: bold; -} - -#tocContainer > ul#toc > li > .sectionName a:hover { - text-decoration: none; -} - -#tocContainer > ul#toc li.children > ul { - display: none; - height: 0; -} - -#tocContainer > ul#toc > li > ul { - margin: 0; - padding: 0; -} - -#tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { - margin-left: 0; - margin-bottom: 0; - padding-left: 15px; -} - -#tocContainer > ul#toc > li ul { - list-style: none; - margin-right: 0; - padding-right: 0; -} - -#tocContainer > ul#toc li.children.open > ul { - display: block; - height: auto; - margin-left: -15px; - padding-left: 0; -} - -#tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { - margin-left: 0; - padding-left: 15px; -} - -#tocContainer li ul li { - margin-top: 0.583em; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -#tocContainer li ul li span.sectionName { - white-space: normal; -} - -#tocContainer > ul#toc > li > ul > li > .sectionName a { - font-weight: bold; -} - -#tocContainer > ul#toc > li > ul a { - color: #4F4F4F; -} - -/* @end */ - -/* @group Index formatting */ - -.index-title { - font-size: 13px; - font-weight: normal; -} - -.index-column { - float: left; - width: 30%; - min-width: 200px; - font-size: 11px; -} - -.index-column ul { - margin: 8px 0 0 0; - padding: 0; - list-style: none; -} - -.index-column ul li { - margin: 0 0 3px 0; - padding: 0; -} - -.hierarchy-column { - min-width: 400px; -} - -.hierarchy-column ul { - margin: 3px 0 0 15px; -} - -.hierarchy-column ul li { - list-style-type: square; -} - -/* @end */ - -/* @group Common formatting elements */ - -.title { - font-weight: normal; - font-size: 215%; - margin-top:0; -} - -.subtitle { - font-weight: normal; - font-size: 180%; - color: #3C4C6C; - border-bottom: 1px solid #5088C5; -} - -.subsubtitle { - font-weight: normal; - font-size: 145%; - height: 0.7em; -} - -.note { - border: 1px solid #5088C5; - background-color: white; - margin: 1.667em 0 1.75em 0; - padding: 0 .667em .083em .750em; -} - -.warning { - border: 1px solid #5088C5; - background-color: #F0F3F7; - margin-bottom: 0.5em; - padding: 0.3em 0.8em; -} - -.bug { - border: 1px solid #000; - background-color: #ffffcc; - margin-bottom: 0.5em; - padding: 0.3em 0.8em; -} - -.deprecated { - color: #F60425; -} - -/* @end */ - -/* @group Common layout */ - -.section { - margin-top: 3em; -} - -/* @end */ - -/* @group Object specification section */ - -.section-specification { - margin-left: 2.5em; - margin-right: 2.5em; - font-size: 12px; -} - -.section-specification table { - margin-bottom: 0em; - border-top: 1px solid #d6e0e5; -} - -.section-specification td { - vertical-align: top; - border-bottom: 1px solid #d6e0e5; - border-left-width: 0px; - border-right-width: 0px; - border-top-width: 0px; - padding: .6em; -} - -.section-specification .specification-title { - font-weight: bold; -} - -/* @end */ - -/* @group Tasks section */ - -.task-list { - list-style-type: none; - padding-left: 0px; -} - -.task-list li { - margin-bottom: 3px; -} - -.task-item-suffix { - color: #996; - font-size: 12px; - font-style: italic; - margin-left: 0.5em; -} - -span.tooltip span.tooltip { - font-size: 1.0em; - display: none; - padding: 0.3em; - border: 1px solid #aaa; - background-color: #fdfec8; - color: #000; - text-align: left; -} - -span.tooltip:hover span.tooltip { - display: block; - position: absolute; - margin-left: 2em; -} - -/* @end */ - -/* @group Method section */ - -.section-method { - margin-top: 2.3em; -} - -.method-title { - margin-bottom: 1.5em; -} - -.method-subtitle { - margin-top: 0.7em; - margin-bottom: 0.2em; -} - -.method-subsection p { - margin-top: 0.4em; - margin-bottom: 0.8em; -} - -.method-declaration { - margin-top:1.182em; - margin-bottom:.909em; -} - -.method-declaration code { - font:14px Courier, Consolas, monospace; - color:#000; -} - -.declaration { - color: #000; -} - -.termdef { - margin-bottom: 10px; - margin-left: 0px; - margin-right: 0px; - margin-top: 0px; -} - -.termdef dt { - margin: 0; - padding: 0; -} - -.termdef dd { - margin-bottom: 6px; - margin-left: 16px; - margin-right: 0px; - margin-top: 1px; -} - -.termdef dd p { - margin-bottom: 6px; - margin-left: 0px; - margin-right: 0px; - margin-top: -1px; -} - -.argument-def { - margin-top: 0.3em; - margin-bottom: 0.3em; -} - -.argument-def dd { - margin-left: 1.25em; -} - -.see-also-section ul { - list-style-type: none; - padding-left: 0px; - margin-top: 0; -} - -.see-also-section li { - margin-bottom: 3px; -} - -.declared-in-ref { - color: #666; -} - -#tocContainer.hideInXcode { - display: none; - border: 0px solid black; -} - -#top_header.hideInXcode { - display: none; -} - -#contents.hideInXcode { - border: 0px solid black; - top: 0px; - left: 0px; -} - -/* @end */ - diff --git a/SQLClient/SQLClientDocs/html/css/stylesPrint.css b/SQLClient/SQLClientDocs/html/css/stylesPrint.css deleted file mode 100644 index dc54cd2..0000000 --- a/SQLClient/SQLClientDocs/html/css/stylesPrint.css +++ /dev/null @@ -1,22 +0,0 @@ - -header { - display: none; -} - -div.main-navigation, div.navigation-top { - display: none; -} - -div#overview_contents, div#contents.isShowingTOC, div#contents { - overflow: visible; - position: relative; - top: 0px; - border: none; - left: 0; -} -#tocContainer.isShowingTOC { - display: none; -} -nav { - display: none; -} \ No newline at end of file diff --git a/SQLClient/SQLClientDocs/html/hierarchy.html b/SQLClient/SQLClientDocs/html/hierarchy.html deleted file mode 100644 index 5b6a6d3..0000000 --- a/SQLClient/SQLClientDocs/html/hierarchy.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - SQLClient Hierarchy - - - - - -
- - - - -
-
-
- - -
- -
-

Class Hierarchy

- - - -
- - - -
- -

Protocol References

- - - - -
- -
- - -
-
- - \ No newline at end of file diff --git a/SQLClient/SQLClientDocs/html/img/button_bar_background.png b/SQLClient/SQLClientDocs/html/img/button_bar_background.png deleted file mode 100644 index 71d1019bc0c5c571dea84ff9775aa243444d2d4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2825 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 b18@fb!nF!3r##!100000NkvXXu0mjf;Lkd# diff --git a/SQLClient/SQLClientDocs/html/img/disclosure.png b/SQLClient/SQLClientDocs/html/img/disclosure.png deleted file mode 100644 index 4c5cbf445602efefe8ee0475703d36ba4132ff21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115 zcmeAS@N?(olHy`uVBq!ia0vp^oIuRM!2%>3kI32qDJxGG#}JO_`KyqCDY z*KXE?%k|6ufBPK!<6_I4f7W}-SGbGsdN0tg!V=WD=wjWz$u?adUkEVRH#$H4{$dk9 P&=dwwS3j3^P63kI32qDKAeK#}JO|$v@6N$mnnQFZs`y zVdCMs<^KgT?B%tdsPGFGRJ=dLFZ%0zg1x*>$p86_dQT3$|8Vc2oeOUWM@HOU=Rfsq fB8n*;t{e<2Cp(|jRn^)AG?l^A)z4*}Q$iB}AA>A0 diff --git a/SQLClient/SQLClientDocs/html/img/library_background.png b/SQLClient/SQLClientDocs/html/img/library_background.png deleted file mode 100644 index 3006248afe8822e2c9761d9e76a900b2e9781366..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!3HEfN&F!HD`A7 zGAIf#2eRk&s%13)vlW+lq|A78-&7mDt%)5zO3YC!PX6C6d@N^ihs=xXE6;3R6D_$Y ZpF#6xy3}8Re4y=ES4z)+>iz|hdl!0_`wkbcR)P-?)y@G60U!DwdS5Kjv*DdV!92v z7!-Jp7=8I~ult4BS5`!1y1 - - - - SQLClient Reference - - - - - -
- - - - -
-
-
- - -
- - - - - -
-

Class References

- -
- - - -
- -

Protocol References

- - - - -
- -
- - -
-
- - \ No newline at end of file From feeac4a7dbc61d453984fe91418d4c384d10c606 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 17:04:26 -0400 Subject: [PATCH 075/127] Removed builds script for generating appledocs --- SQLClient/SQLClient.xcodeproj/project.pbxproj | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/SQLClient/SQLClient.xcodeproj/project.pbxproj b/SQLClient/SQLClient.xcodeproj/project.pbxproj index 72f94d5..95e1867 100644 --- a/SQLClient/SQLClient.xcodeproj/project.pbxproj +++ b/SQLClient/SQLClient.xcodeproj/project.pbxproj @@ -204,7 +204,6 @@ 5B0FC847180DCEB000DF4EFE /* Sources */, 5B0FC848180DCEB000DF4EFE /* Frameworks */, 5B0FC849180DCEB000DF4EFE /* Resources */, - 5B0FC8A1180DDBFE00DF4EFE /* ShellScript */, ); buildRules = ( ); @@ -287,22 +286,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 5B0FC8A1180DDBFE00DF4EFE /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "#Update build number with number of git commits if in release mode\nif [ ${CONFIGURATION} == \"Release\" ]; then\nbuildNumber=$(git rev-list HEAD | wc -l | tr -d ' ')\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"${PROJECT_DIR}/${INFOPLIST_FILE}\"\nfi;\n\nif [ ${CONFIGURATION} == \"Release\" ]; then\nAPPLEDOC_PATH=`which appledoc`\nif [ $APPLEDOC_PATH ]; then\n$APPLEDOC_PATH \\\n--project-name ${PRODUCT_NAME} \\\n--project-company \"Martin Rybak\" \\\n--company-id \"com.martinrybak\" \\\n--output ${PRODUCT_NAME}Docs \\\n--keep-intermediate-files \\\n--ignore \"*.m\" \\\n--no-install-docset \\\n--no-repeat-first-par \\\n--no-warn-invalid-crossref \\\n--exit-threshold 2 \\\n${PROJECT_DIR}/${PRODUCT_NAME}\nfi;\nfi;"; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 5B0FC847180DCEB000DF4EFE /* Sources */ = { isa = PBXSourcesBuildPhase; From 8ca922c47bb4b82b7ec2660760f951c20bd13e40 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 25 Oct 2016 17:04:40 -0400 Subject: [PATCH 076/127] Updated podspec --- SQLClient.podspec | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/SQLClient.podspec b/SQLClient.podspec index 828219a..595a35d 100644 --- a/SQLClient.podspec +++ b/SQLClient.podspec @@ -1,16 +1,15 @@ Pod::Spec.new do |s| s.name = 'SQLClient' - s.version = '0.1.3' + s.version = '1.0.0' s.license = 'MIT' s.summary = 'An Objective-C wrapper around the open-source FreeTDS library' - s.homepage = 'http://htmlpreview.github.io/?https://github.com/martinrybak/SQLClient/blob/0.1.0/SQLClient/SQLClientDocs/html/index.html' + s.homepage = 'https://github.com/martinrybak/SQLClient' s.authors = { 'Martin Rybak' => 'martin.rybak@gmail.com' } s.source = { :git => 'https://github.com/martinrybak/SQLClient.git', :tag => s.version.to_s } s.source_files = 'SQLClient/SQLClient/SQLClient/*.{h,m}' s.vendored_libraries = 'SQLClient/SQLClient/SQLClient/libsybdb.a' s.libraries = 'iconv' s.requires_arc = true - s.ios.deployment_target = '7.0' s.tvos.deployment_target = '9.0' end From c397b5771a926afdc4d2c755749cbdd71f6f06ec Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 26 Oct 2016 08:19:01 -0400 Subject: [PATCH 077/127] Limit varchar(max) to 8000 --- README.md | 2 +- SQLClient/SQLClient/SQLClient/SQLClient.m | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5d335a8..8b23db8 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ SQLClient maps SQL Server data types into the following native Objective-C objec * uniqueidentifier → NSUUID * varbinary → NSData * varbinary(max) → NSData -* varchar(max) → NSString +* varchar(max) → NSString **(max length 8000)** * varchar(n) → NSString * xml → NSString diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 3dfcbfe..0e5e9c6 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -14,6 +14,7 @@ #define SYBUNIQUEIDENTIFIER 36 int const SQLClientDefaultTimeout = 5; +int const SQLClientVarcharMaxLength = 8000; //VARCHAR(N) max length NSString* const SQLClientDefaultCharset = @"UTF-8"; NSString* const SQLClientWorkerQueueName = @"com.martinrybak.sqlclient"; NSString* const SQLClientPendingConnectionError = @"Attempting to connect while a connection is active."; @@ -249,14 +250,6 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu column->type = dbcoltype(_connection, c); column->size = dbcollen(_connection, c); - //Create buffer for column data - column->data = calloc(1, column->size); - if (!column->data) { - [self executionFailure:completion]; - [self cleanupAfterExecution:numColumns]; - return; - } - //Set bind type based on column type int varType = CHARBIND; //Default switch (column->type) @@ -319,6 +312,7 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu case SYBNTEXT: { varType = NTBSTRINGBIND; + column->size = MIN(column->size, SQLClientVarcharMaxLength); break; } case SYBDATETIME: @@ -347,6 +341,14 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu break; } } + + //Create space for column data + column->data = calloc(1, column->size); + if (!column->data) { + [self executionFailure:completion]; + [self cleanupAfterExecution:numColumns]; + return; + } //Bind column data _returnCode = dbbind(_connection, c, varType, column->size, column->data); From 2dc24bc3e944a22bbb537b56bedf7f82119e525d Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 26 Oct 2016 16:08:12 -0400 Subject: [PATCH 078/127] Updated readme --- README.md | 58 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8b23db8..9002f30 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,30 @@ SQLClient* client = [SQLClient sharedInstance]; }]; +##Errors + +Errors are communicated via `NSNotificationCenter`: + +``` +[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(error:) name:SQLClientErrorNotification object:nil]; +[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(message:) name:SQLClientMessageNotification object:nil]; + +- (void)error:(NSNotification*)notification +{ + NSNumber* code = notification.userInfo[SQLClientCodeKey]; + NSString* message = notification.userInfo[SQLClientMessageKey]; + NSNumber* severity = notification.userInfo[SQLClientSeverityKey]; + + NSLog(@"Error #%@: %@ (Severity %@)", code, message, severity); +} + +- (void)message:(NSNotification*)notification +{ + NSString* message = notification.userInfo[SQLClientMessageKey]; + NSLog(@"Message: %@", message); +} +``` + ##Type Conversion SQLClient maps SQL Server data types into the following native Objective-C objects: @@ -33,10 +57,10 @@ SQLClient maps SQL Server data types into the following native Objective-C objec * bit → NSNumber * char(n) → NSString * cursor → **not supported** -* date → **NSString** +* date → **NSDate** or **NSString**† * datetime → NSDate -* datetime2 → **NSString** -* datetimeoffset → **NSString** +* datetime2 → **NSDate** or **NSString**† +* datetimeoffset → **NSDate** or **NSString**† * decimal(p,s) → NSNumber * float(n) → NSNumber * image → UIImage @@ -54,8 +78,8 @@ SQLClient maps SQL Server data types into the following native Objective-C objec * sql_variant → **not supported** * table → **not supported** * text → NSString -* time → **NSString** -* timestamp → **NSData** +* time → **NSDate** or **NSString**† +* timestamp → **NSDate** or **NSString**† * tinyint → NSNumber * uniqueidentifier → NSUUID * varbinary → NSData @@ -64,11 +88,26 @@ SQLClient maps SQL Server data types into the following native Objective-C objec * varchar(n) → NSString * xml → NSString +†By default FreeTDS uses version **7.1** of the TDS protocol. The following data types are only converted to **NSDate** with version **7.3** and higher. On lower versions, they will be converted to **NSString**. + +* date +* datetime2 +* datetimeoffset +* time +* timestamp + +To use a higher version of the TDS protocol, add an environment variable to Xcode named `TDSVER`. Possible values are +`4.2`, `5.0`, `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `auto`. +A value of `auto` tells FreeTDS to use an autodetection (trial-and-error) algorithm to choose the protocol version. + ##Testing The `SQLClientTests` target contains integration tests which require a connection to an instance of SQL Server. The intergration tests have passed successfully on the following database servers: -* SQL Server 2008 R2 +* SQL Server 7.0 (TDS 7.0) +* SQL Server 2000 (TDS 7.1) +* SQL Server 2005 (TDS 7.2) +* SQL Server 2008 (TDS 7.3) * **TODO: add more!** To configure the connection for your server: @@ -86,11 +125,6 @@ To configure the connection for your server: PR's welcome! * **money**: FreeTDS will truncate the rightmost 2 digits. -* The following data types are recognized by FreeTDS as type **SYBCHAR**, so SQLClient can't convert them into proper **NSDate** objects: - * date - * datetime2 - * datetimeoffset - * time * OSX support: [FreeTDS-iOS](https://github.com/martinrybak/FreeTDS-iOS) needs to be compiled to support OSX and Podspec updated * No support for stored procedures with out parameters (yet) * No support for returning number of rows changed (yet) @@ -109,7 +143,7 @@ Open the Xcode project inside the **SQLClient** folder. 1. Open a Terminal window. Update RubyGems by entering: `sudo gem update --system`. Enter your password when prompted. 2. Install CocoaPods by entering `sudo gem install cocoapods`. 3. Create a file at the root of your Xcode project folder called **Podfile**. -4. Enter the following text: `pod 'SQLClient', '~> 0.1.3'` +4. Enter the following text: `pod 'SQLClient', '~> 1.0.0'` 4. In Terminal navigate to this folder and enter `pod install`. 5. You will see a new **SQLClient.xcworkspace** file. Open this file in Xcode to work with this project from now on. From 01c4e5704f308547218edc4be5c5439e2c0dc181 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 26 Oct 2016 16:13:02 -0400 Subject: [PATCH 079/127] Updates to readme --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9002f30..a0ca562 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Native Microsoft SQL Server client for iOS. An Objective-C wrapper around the op #import "SQLClient.h" SQLClient* client = [SQLClient sharedInstance]; -[client connect:@"server:port" username:@"user" password:@"pass" database:@"db" completion:^(BOOL success) { +[client connect:@"server\instance:port" username:@"user" password:@"pass" database:@"db" completion:^(BOOL success) { if (success) { [client execute:@"SELECT * FROM Users" completion:^(NSArray* results) { for (NSArray* table in results) { @@ -27,7 +27,7 @@ SQLClient* client = [SQLClient sharedInstance]; ##Errors -Errors are communicated via `NSNotificationCenter`: +FreeTDS communicates errors and messages. Both are communicated via `NSNotificationCenter`: ``` [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(error:) name:SQLClientErrorNotification object:nil]; @@ -50,7 +50,7 @@ Errors are communicated via `NSNotificationCenter`: ``` ##Type Conversion -SQLClient maps SQL Server data types into the following native Objective-C objects: +SQLClient maps SQL Server data types into the following native Objective-C types: * bigint → NSNumber * binary(n) → NSData @@ -102,7 +102,7 @@ A value of `auto` tells FreeTDS to use an autodetection (trial-and-error) algori ##Testing -The `SQLClientTests` target contains integration tests which require a connection to an instance of SQL Server. The intergration tests have passed successfully on the following database servers: +The `SQLClientTests` target contains integration tests which require a connection to an instance of SQL Server. The integration tests have passed successfully on the following database servers: * SQL Server 7.0 (TDS 7.0) * SQL Server 2000 (TDS 7.1) @@ -115,7 +115,7 @@ To configure the connection for your server: * In Xcode, go to `Edit Scheme...` and select the `Test` scheme. * On the `Arguments` tab, uncheck `Use the Run action's arguments and environment variables` * Add the following environment variables for your server. The values should be the same as you pass in to the `connect:` method. - * `HOST` (including port, i.e. `192.168.1.1:1433`) + * `HOST` (`server\instance:port`) * `DATABASE` (optional) * `USERNAME` * `PASSWORD` From 4813b53cc62b423a1908325b6ac5a3389ada0db6 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 26 Oct 2016 16:14:47 -0400 Subject: [PATCH 080/127] Tweaks to readme --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index a0ca562..e8d6f87 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ SQLClient* client = [SQLClient sharedInstance]; ##Errors -FreeTDS communicates errors and messages. Both are communicated via `NSNotificationCenter`: +FreeTDS communicates both errors and messages. Both are broadcast via `NSNotificationCenter`: ``` [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(error:) name:SQLClientErrorNotification object:nil]; @@ -38,7 +38,6 @@ FreeTDS communicates errors and messages. Both are communicated via `NSNotificat NSNumber* code = notification.userInfo[SQLClientCodeKey]; NSString* message = notification.userInfo[SQLClientMessageKey]; NSNumber* severity = notification.userInfo[SQLClientSeverityKey]; - NSLog(@"Error #%@: %@ (Severity %@)", code, message, severity); } From 71a9a94e3e95259ab7258df73e06907f6146296c Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 26 Oct 2016 16:19:14 -0400 Subject: [PATCH 081/127] More readme updates --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e8d6f87..f5bf9c1 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ SQLClient* client = [SQLClient sharedInstance]; ##Errors -FreeTDS communicates both errors and messages. Both are broadcast via `NSNotificationCenter`: +FreeTDS communicates both errors and messages. `SQLClient` rebroadcasts both via `NSNotificationCenter`: ``` [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(error:) name:SQLClientErrorNotification object:nil]; From 72550887cb0d769162c15adb1d7217ce906f3d1e Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 26 Oct 2016 16:31:38 -0400 Subject: [PATCH 082/127] Updated comments and formatting --- SQLClient/SQLClient/SQLClient/SQLClient.m | 30 +++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 0e5e9c6..dd61178 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -320,7 +320,7 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu case SYBDATETIMN: case SYBBIGDATETIME: case SYBBIGTIME: - //FreeTDS incorrectly identifies the following types as SYBCHAR: + //TDS protocol version < 7.3 identifies the following types as SYBCHAR: case SYBDATE: case SYBTIME: case SYBMSDATE: @@ -505,7 +505,7 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu row[columnName] = value; } - //Add an immutable copy to the table + //Add an immutable copy to the table [table addObject:[row copy]]; break; } @@ -529,7 +529,7 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu [output addObject:[table copy]]; } - //Success! Send an immutable copy of the results array + //Success! Send an immutable copy of the results array [self executionSuccess:completion results:[output copy]]; [self cleanupAfterExecution:numColumns]; }]; @@ -618,43 +618,43 @@ - (void)cleanupAfterExecution:(int)numColumns //Invokes connection completion handler on callback queue with success = NO - (void)connectionFailure:(void (^)(BOOL success))completion { - [self.callbackQueue addOperationWithBlock:^{ + [self.callbackQueue addOperationWithBlock:^{ if (completion) { - completion(NO); + completion(NO); } - }]; + }]; } //Invokes connection completion handler on callback queue with success = [self connected] - (void)connectionSuccess:(void (^)(BOOL success))completion { - [self.callbackQueue addOperationWithBlock:^{ + [self.callbackQueue addOperationWithBlock:^{ if (completion) { - completion([self isConnected]); + completion([self isConnected]); } - }]; + }]; } //Invokes execution completion handler on callback queue with results = nil - (void)executionFailure:(void (^)(NSArray* results))completion { self.executing = NO; - [self.callbackQueue addOperationWithBlock:^{ + [self.callbackQueue addOperationWithBlock:^{ if (completion) { - completion(nil); + completion(nil); } - }]; + }]; } //Invokes execution completion handler on callback queue with results array - (void)executionSuccess:(void (^)(NSArray* results))completion results:(NSArray*)results { self.executing = NO; - [self.callbackQueue addOperationWithBlock:^{ + [self.callbackQueue addOperationWithBlock:^{ if (completion) { - completion(results); + completion(results); } - }]; + }]; } #pragma mark - Reference Date From b723022ed62e70155bf930c6c79eb2368c9aea2a Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 26 Oct 2016 16:33:30 -0400 Subject: [PATCH 083/127] Updated comment --- SQLClient/SQLClient/SQLClient/SQLClient.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index dd61178..f49bb71 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -40,7 +40,7 @@ @interface SQLClient () @property (nonatomic, strong) NSOperationQueue* workerQueue; @property (nonatomic, weak) NSOperationQueue* callbackQueue; -@property (atomic, assign, getter=isExecuting) BOOL executing; //Atomic because can be called from public API on main thread +@property (atomic, assign, getter=isExecuting) BOOL executing; //Atomic because can be called from public API on !self.workerQueue @end From fdcacf1198007059943cba39673212a3f160994a Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 26 Oct 2016 16:34:56 -0400 Subject: [PATCH 084/127] Updated comment --- SQLClient/SQLClient/SQLClient/SQLClient.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index f49bb71..04febd1 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -60,7 +60,7 @@ - (id)init { if (self = [super init]) { - //Initialize the FreeTDS library + //Initialize the FreeTDS library if (dbinit() == FAIL) { return nil; } From 39fc8c4d0c7110db3f2b95b37ca11997e4d15293 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 26 Oct 2016 21:58:51 -0400 Subject: [PATCH 085/127] Added more date conversion --- SQLClient/SQLClient/SQLClient/SQLClient.m | 88 ++++++++++++++++++----- 1 file changed, 71 insertions(+), 17 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 04febd1..9673abb 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -320,15 +320,26 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu case SYBDATETIMN: case SYBBIGDATETIME: case SYBBIGTIME: - //TDS protocol version < 7.3 identifies the following types as SYBCHAR: + { + varType = DATETIMEBIND; + break; + } case SYBDATE: - case SYBTIME: case SYBMSDATE: + { + varType = DATEBIND; + break; + } + case SYBTIME: case SYBMSTIME: - case SYBMSDATETIME2: + { + varType = TIMEBIND; + break; + } case SYBMSDATETIMEOFFSET: + case SYBMSDATETIME2: { - varType = DATETIMEBIND; + varType = DATETIME2BIND; break; } case SYBVOID: @@ -466,18 +477,37 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu value = [NSString stringWithUTF8String:(char*)column->data]; break; } - case SYBDATETIME: - case SYBDATETIME4: + case SYBDATETIME: //From January 1, 1753 to December 31, 9999 with an accuracy of 3.33 milliseconds case SYBDATETIMN: case SYBBIGDATETIME: case SYBBIGTIME: + case SYBMSDATETIME2: //From January 1, 0001 to December 31, 9999 with an accuracy of 100 nanoseconds + case SYBMSDATETIMEOFFSET: //The same as SYBMSDATETIME2 with the addition of a time zone offset { - DBDATETIME _value; - memcpy(&_value, column->data, sizeof _value); - NSTimeInterval daysSinceReferenceDate = (NSTimeInterval)_value.dtdays; //Days are counted from 1/1/1900 - NSTimeInterval secondsSinceReferenceDate = daysSinceReferenceDate * 24 * 60 * 60; - NSTimeInterval secondsSinceMidnight = _value.dttime / 3000; //Time is in increments of 3.33 milliseconds - value = [NSDate dateWithTimeInterval:secondsSinceReferenceDate + secondsSinceMidnight sinceDate:[self referenceDate]]; + DBDATEREC2 _value; + dbanydatecrack(_connection, &_value, column->type, column->data); + value = [self dateWithYear:_value.dateyear month:_value.datemonth + 1 day:_value.datedmonth hour:_value.datehour minute:_value.dateminute second:_value.datesecond nanosecond:_value.datensecond timezone:_value.datetzone]; + break; + } + case SYBDATETIME4: //From January 1, 1900 to June 6, 2079 with an accuracy of 1 minute + { + DBDATEREC _value; + dbdatecrack(_connection, &_value, (DBDATETIME*)column->data); + value = [self dateWithYear:_value.dateyear month:_value.datemonth + 1 day:_value.datedmonth hour:_value.datehour minute:_value.dateminute second:_value.datesecond]; + break; + } + case SYBMSDATE: //Store a date only. From January 1, 0001 to December 31, 9999 + { + DBDATEREC _value; + dbdatecrack(_connection, &_value, (DBDATETIME*)column->data); + value = [self dateWithYear:_value.dateyear month:_value.datemonth + 1 day:_value.datedmonth]; + break; + } + case SYBMSTIME: //Store a time only to an accuracy of 100 nanoseconds + { + DBDATEREC2 _value; + dbanydatecrack(_connection, &_value, column->type, column->data); + value = [self dateWithYear:_value.dateyear month:_value.datemonth + 1 day:_value.datedmonth hour:_value.datehour minute:_value.dateminute second:_value.datesecond nanosecond:_value.datensecond timezone:_value.datetzone]; break; } case SYBVOID: @@ -657,16 +687,40 @@ - (void)executionSuccess:(void (^)(NSArray* results))completion results:(NSArray }]; } -#pragma mark - Reference Date +#pragma mark - Date -//January 1, 1900 - (NSDate*)referenceDate +{ + return [self dateWithYear:1900 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0]; +} + +- (NSDate*)dateWithYear:(int)year month:(int)month day:(int)day +{ + return [self dateWithYear:year month:month day:day hour:0 minute:0 second:0]; +} + +- (NSDate*)dateWithYear:(int)year month:(int)month day:(int)day hour:(int)hour minute:(int)minute second:(int)second +{ + return [self dateWithYear:year month:month day:day hour:hour minute:minute second:second nanosecond:0]; +} + +- (NSDate*)dateWithYear:(int)year month:(int)month day:(int)day hour:(int)hour minute:(int)minute second:(int)second nanosecond:(int)nanosecond +{ + return [self dateWithYear:year month:month day:day hour:hour minute:minute second:second nanosecond:0 timezone:0]; +} + +- (NSDate*)dateWithYear:(int)year month:(int)month day:(int)day hour:(int)hour minute:(int)minute second:(int)second nanosecond:(int)nanosecond timezone:(int)timezone { NSCalendar* calendar = [NSCalendar currentCalendar]; NSDateComponents* dateComponents = [[NSDateComponents alloc] init]; - dateComponents.year = 1900; - dateComponents.month = 1; - dateComponents.day = 1; + dateComponents.year = year; + dateComponents.month = month; + dateComponents.day = day; + dateComponents.hour = hour; + dateComponents.minute = minute; + dateComponents.second = second; + dateComponents.nanosecond = nanosecond; + dateComponents.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:timezone * 60]; return [calendar dateFromComponents:dateComponents]; } From 4b8253c6df3021bfe39212f71dd825f1fb3e3449 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 26 Oct 2016 22:08:07 -0400 Subject: [PATCH 086/127] Update readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f5bf9c1..d9ff1c4 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ SQLClient maps SQL Server data types into the following native Objective-C types * float(n) → NSNumber * image → UIImage * int → NSNumber -* money → NSDecimalNumber **(last 2 digits are truncated)** +* money → NSDecimalNumber **(last 2**** digits are truncated)** * nchar → NSString * ntext → NSString * numeric(p,s) → NSNumber @@ -78,7 +78,7 @@ SQLClient maps SQL Server data types into the following native Objective-C types * table → **not supported** * text → NSString * time → **NSDate** or **NSString**† -* timestamp → **NSDate** or **NSString**† +* timestamp → NSData * tinyint → NSNumber * uniqueidentifier → NSUUID * varbinary → NSData @@ -93,7 +93,6 @@ SQLClient maps SQL Server data types into the following native Objective-C types * datetime2 * datetimeoffset * time -* timestamp To use a higher version of the TDS protocol, add an environment variable to Xcode named `TDSVER`. Possible values are `4.2`, `5.0`, `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `auto`. @@ -123,6 +122,7 @@ To configure the connection for your server: ## Known Issues PR's welcome! +* **time**: FreeTDS does not provide correct hour/minute/second/nanosecond values * **money**: FreeTDS will truncate the rightmost 2 digits. * OSX support: [FreeTDS-iOS](https://github.com/martinrybak/FreeTDS-iOS) needs to be compiled to support OSX and Podspec updated * No support for stored procedures with out parameters (yet) From 514218041cf6db07aed4b0401e2c6c7d8fe73f06 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Thu, 27 Oct 2016 08:16:15 -0400 Subject: [PATCH 087/127] Updated readme --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d9ff1c4..6d424fc 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ SQLClient maps SQL Server data types into the following native Objective-C types * float(n) → NSNumber * image → UIImage * int → NSNumber -* money → NSDecimalNumber **(last 2**** digits are truncated)** +* money → NSDecimalNumber **(last 2 digits are truncated)** * nchar → NSString * ntext → NSString * numeric(p,s) → NSNumber @@ -87,17 +87,15 @@ SQLClient maps SQL Server data types into the following native Objective-C types * varchar(n) → NSString * xml → NSString -†By default FreeTDS uses version **7.1** of the TDS protocol. The following data types are only converted to **NSDate** with version **7.3** and higher. On lower versions, they will be converted to **NSString**. +†The following data types are only converted to **NSDate** on TDS version **7.3** and higher. By default FreeTDS uses version **7.1** of the TDS protocol, which converts them to **NSString**. To use a higher version of the TDS protocol, add an environment variable to Xcode named `TDSVER`. Possible values are +`4.2`, `5.0`, `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `auto`. +A value of `auto` tells FreeTDS to use an autodetection (trial-and-error) algorithm to choose the highest available protocol version. * date * datetime2 * datetimeoffset * time -To use a higher version of the TDS protocol, add an environment variable to Xcode named `TDSVER`. Possible values are -`4.2`, `5.0`, `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `auto`. -A value of `auto` tells FreeTDS to use an autodetection (trial-and-error) algorithm to choose the protocol version. - ##Testing The `SQLClientTests` target contains integration tests which require a connection to an instance of SQL Server. The integration tests have passed successfully on the following database servers: From 2b30c15caf695e0b5c0831f4fa03248e5eb07530 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Thu, 27 Oct 2016 16:30:32 -0400 Subject: [PATCH 088/127] Added money and smallmoney tests --- SQLClient/SQLClientTests/SQLClientTests.m | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index ace9e37..efa6c44 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -75,6 +75,33 @@ - (void)testBigInt [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } +- (void)testSmallMoney +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT SmallMoney FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"SmallMoney"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"SmallMoney"], [NSDecimalNumber decimalNumberWithString:@"-214748.3648"]); + XCTAssertEqualObjects(results[0][2][@"SmallMoney"], [NSDecimalNumber decimalNumberWithString:@"214748.3647"]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testMoney +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT Money FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Money"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"Money"], [NSDecimalNumber decimalNumberWithString:@"-922337203685477.58"]); + XCTAssertEqualObjects(results[0][2][@"Money"], [NSDecimalNumber decimalNumberWithString:@"922337203685477.58"]); + //TODO: fix last 2 digits truncated + //XCTAssertEqualObjects(results[0][1][@"Money"], [NSDecimalNumber decimalNumberWithString:@"-922337203685477.5808"]); + //XCTAssertEqualObjects(results[0][2][@"Money"], [NSDecimalNumber decimalNumberWithString:@"922337203685477.5807"]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + #pragma mark - Private - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion From 39940d53b5a3333ecb1d116686a6508b6f334f3f Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 28 Oct 2016 15:03:00 -0400 Subject: [PATCH 089/127] Fixed time handling by converting to string and parsing --- SQLClient/SQLClient/SQLClient/SQLClient.m | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 9673abb..fef0b05 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -333,7 +333,9 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu case SYBTIME: case SYBMSTIME: { - varType = TIMEBIND; + //Workaround for TIME data type. We have to increase the size and cast as string. + column->size += 14; + varType = CHARBIND; break; } case SYBMSDATETIMEOFFSET: @@ -505,9 +507,21 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu } case SYBMSTIME: //Store a time only to an accuracy of 100 nanoseconds { - DBDATEREC2 _value; - dbanydatecrack(_connection, &_value, column->type, column->data); - value = [self dateWithYear:_value.dateyear month:_value.datemonth + 1 day:_value.datedmonth hour:_value.datehour minute:_value.dateminute second:_value.datesecond nanosecond:_value.datensecond timezone:_value.datetzone]; + //Extract time components out of string since DBDATEREC conversion does not work + NSString* string = [NSString stringWithUTF8String:(char*)column->data]; + NSString* time = [string substringFromIndex:string.length - 18]; + int hour = [[time substringWithRange:NSMakeRange(0, 2)] integerValue]; + int minute = [[time substringWithRange:NSMakeRange(3, 2)] integerValue]; + int second = [[time substringWithRange:NSMakeRange(6, 2)] integerValue]; + int nanosecond = [[time substringWithRange:NSMakeRange(9, 7)] integerValue]; + NSString* meridian = [time substringWithRange:NSMakeRange(16, 2)]; + if ([meridian isEqualToString:@"AM"]) { + hour -= 12; + } else { + hour += 12; + } + + value = [self dateWithYear:1900 month:1 day:1 hour:hour minute:minute second:second nanosecond:nanosecond * 100 timezone:0]; break; } case SYBVOID: From b6a814522d7c56fefcdd54bc8a2b46436ed09706 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 28 Oct 2016 15:03:09 -0400 Subject: [PATCH 090/127] Updated comments --- SQLClient/SQLClient/SQLClient/SQLClient.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index fef0b05..61add84 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -479,11 +479,11 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu value = [NSString stringWithUTF8String:(char*)column->data]; break; } - case SYBDATETIME: //From January 1, 1753 to December 31, 9999 with an accuracy of 3.33 milliseconds + case SYBDATETIME: //From 1/1/1753 00:00:00.000 to 12/31/9999 23:59:59.997 with an accuracy of 3.33 milliseconds case SYBDATETIMN: case SYBBIGDATETIME: case SYBBIGTIME: - case SYBMSDATETIME2: //From January 1, 0001 to December 31, 9999 with an accuracy of 100 nanoseconds + case SYBMSDATETIME2: //From 1/1/0001 00:00:00 to 12/31/9999 23:59:59.9999999 with an accuracy of 100 nanoseconds case SYBMSDATETIMEOFFSET: //The same as SYBMSDATETIME2 with the addition of a time zone offset { DBDATEREC2 _value; @@ -491,21 +491,21 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu value = [self dateWithYear:_value.dateyear month:_value.datemonth + 1 day:_value.datedmonth hour:_value.datehour minute:_value.dateminute second:_value.datesecond nanosecond:_value.datensecond timezone:_value.datetzone]; break; } - case SYBDATETIME4: //From January 1, 1900 to June 6, 2079 with an accuracy of 1 minute + case SYBDATETIME4: //From January 1, 1900 00:00 to June 6, 2079 23:59 with an accuracy of 1 minute { DBDATEREC _value; dbdatecrack(_connection, &_value, (DBDATETIME*)column->data); value = [self dateWithYear:_value.dateyear month:_value.datemonth + 1 day:_value.datedmonth hour:_value.datehour minute:_value.dateminute second:_value.datesecond]; break; } - case SYBMSDATE: //Store a date only. From January 1, 0001 to December 31, 9999 + case SYBMSDATE: //From January 1, 0001 to December 31, 9999 { DBDATEREC _value; dbdatecrack(_connection, &_value, (DBDATETIME*)column->data); value = [self dateWithYear:_value.dateyear month:_value.datemonth + 1 day:_value.datedmonth]; break; } - case SYBMSTIME: //Store a time only to an accuracy of 100 nanoseconds + case SYBMSTIME: //00:00:00 to 23:59:59.9999999 with an accuracy of 100 nanoseconds { //Extract time components out of string since DBDATEREC conversion does not work NSString* string = [NSString stringWithUTF8String:(char*)column->data]; From e626642d55c8fabad234edb30927b1fb4ca1280a Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 28 Oct 2016 15:03:25 -0400 Subject: [PATCH 091/127] Added date & time tests --- SQLClient/SQLClientTests/SQLClientTests.m | 99 +++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index efa6c44..dac5e2f 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -75,6 +75,13 @@ - (void)testBigInt [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } +/* TODO: + decimal + numeric + float + real + */ + - (void)testSmallMoney { XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; @@ -102,6 +109,83 @@ - (void)testMoney [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } +- (void)testSmallDateTime +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT SmallDateTime FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"SmallDateTime"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"SmallDateTime"], [self dateWithYear:1900 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]); + XCTAssertEqualObjects(results[0][2][@"SmallDateTime"], [self dateWithYear:2079 month:6 day:6 hour:23 minute:59 second:0 nanosecond:0 timezone:0]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testDateTime +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT DateTime FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"DateTime"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"DateTime"], [self dateWithYear:1753 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]); + XCTAssertEqualObjects(results[0][2][@"DateTime"], [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:997000000 timezone:0]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +#pragma mark - TDS 7.3 + +//If these test fail, you must tell FreeTDS to use the TDS protocol >= 7.3. +//Add an environment variable to the test scheme with name TDSVER and value auto + +- (void)testDateTime2 +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT DateTime2 FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"DateTime2"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"DateTime2"], [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]); + XCTAssertEqualObjects(results[0][2][@"DateTime2"], [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:999999900 timezone:0]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testDateTimeOffset +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT DateTimeOffset FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"DateTimeOffset"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"DateTimeOffset"], [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:-840]); + XCTAssertEqualObjects(results[0][2][@"DateTimeOffset"], [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:999999900 timezone:840]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testDate +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT Date FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Date"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"Date"], [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]); + XCTAssertEqualObjects(results[0][2][@"Date"], [self dateWithYear:9999 month:12 day:31 hour:0 minute:0 second:0 nanosecond:0 timezone:0]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testTime +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT Time FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Time"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"Time"], [self dateWithYear:1900 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]); + XCTAssertEqualObjects(results[0][2][@"Time"], [self dateWithYear:1900 month:1 day:1 hour:23 minute:59 second:59 nanosecond:999999900 timezone:0]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:1000 handler:nil]; +} + #pragma mark - Private - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion @@ -128,4 +212,19 @@ - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion }]; } +- (NSDate*)dateWithYear:(int)year month:(int)month day:(int)day hour:(int)hour minute:(int)minute second:(int)second nanosecond:(int)nanosecond timezone:(int)timezone +{ + NSCalendar* calendar = [NSCalendar currentCalendar]; + NSDateComponents* dateComponents = [[NSDateComponents alloc] init]; + dateComponents.year = year; + dateComponents.month = month; + dateComponents.day = day; + dateComponents.hour = hour; + dateComponents.minute = minute; + dateComponents.second = second; + dateComponents.nanosecond = nanosecond; + dateComponents.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:timezone * 60]; + return [calendar dateFromComponents:dateComponents]; +} + @end From 2d348cec535a0edeecf4d81fd267dbc1bc7ddcd1 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 28 Oct 2016 15:04:10 -0400 Subject: [PATCH 092/127] Removed time bug from readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 6d424fc..01ddc44 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,6 @@ To configure the connection for your server: ## Known Issues PR's welcome! -* **time**: FreeTDS does not provide correct hour/minute/second/nanosecond values * **money**: FreeTDS will truncate the rightmost 2 digits. * OSX support: [FreeTDS-iOS](https://github.com/martinrybak/FreeTDS-iOS) needs to be compiled to support OSX and Podspec updated * No support for stored procedures with out parameters (yet) From 9e78541006645f5566fe3085549d0c4db71864fa Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 28 Oct 2016 15:56:04 -0400 Subject: [PATCH 093/127] Added tests for float and real --- SQLClient/SQLClientTests/SQLClientTests.m | 26 +++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index dac5e2f..f6e3312 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -75,11 +75,33 @@ - (void)testBigInt [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } +- (void)testFloat +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT Float FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Float"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"Float"], @(-1.79e+308)); + XCTAssertEqualObjects(results[0][2][@"Float"], @(1.79e+308)); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testReal +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT Real FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Real"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"Real"], [NSNumber numberWithFloat:-3.4e+38]); + XCTAssertEqualObjects(results[0][2][@"Real"], [NSNumber numberWithFloat:3.4e+38]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + /* TODO: decimal numeric - float - real */ - (void)testSmallMoney From 74e823fad64e3e2a04287eedf048979618838439 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 28 Oct 2016 22:20:16 -0400 Subject: [PATCH 094/127] Moved date from time conversion to separate method and fixed meridian logic --- SQLClient/SQLClient/SQLClient/SQLClient.m | 40 +++++++++++++++-------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 61add84..15e7b01 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -509,19 +509,7 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu { //Extract time components out of string since DBDATEREC conversion does not work NSString* string = [NSString stringWithUTF8String:(char*)column->data]; - NSString* time = [string substringFromIndex:string.length - 18]; - int hour = [[time substringWithRange:NSMakeRange(0, 2)] integerValue]; - int minute = [[time substringWithRange:NSMakeRange(3, 2)] integerValue]; - int second = [[time substringWithRange:NSMakeRange(6, 2)] integerValue]; - int nanosecond = [[time substringWithRange:NSMakeRange(9, 7)] integerValue]; - NSString* meridian = [time substringWithRange:NSMakeRange(16, 2)]; - if ([meridian isEqualToString:@"AM"]) { - hour -= 12; - } else { - hour += 12; - } - - value = [self dateWithYear:1900 month:1 day:1 hour:hour minute:minute second:second nanosecond:nanosecond * 100 timezone:0]; + value = [self dateWithTimeString:string]; break; } case SYBVOID: @@ -738,4 +726,30 @@ - (NSDate*)dateWithYear:(int)year month:(int)month day:(int)day hour:(int)hour m return [calendar dateFromComponents:dateComponents]; } +- (NSDate*)dateWithTimeString:(NSString*)string +{ + if (string.length < 30) { + return nil; + } + + NSString* time = [string substringFromIndex:string.length - 18]; + int hour = [[time substringWithRange:NSMakeRange(0, 2)] integerValue]; + int minute = [[time substringWithRange:NSMakeRange(3, 2)] integerValue]; + int second = [[time substringWithRange:NSMakeRange(6, 2)] integerValue]; + int nanosecond = [[time substringWithRange:NSMakeRange(9, 7)] integerValue]; + NSString* meridian = [time substringWithRange:NSMakeRange(16, 2)]; + + if ([meridian isEqualToString:@"AM"]) { + if (hour == 12) { + hour = 0; + } + } else { + if (hour < 12) { + hour += 12; + } + } + + return [self dateWithYear:1900 month:1 day:1 hour:hour minute:minute second:second nanosecond:nanosecond * 100 timezone:0]; +} + @end From e803fe96e81ad26d841602a5f905e4fdd3475126 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 28 Oct 2016 22:22:46 -0400 Subject: [PATCH 095/127] Updated bit test --- SQLClient/SQLClientTests/SQLClientTests.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index f6e3312..0f703ba 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -20,8 +20,8 @@ - (void)testBit XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:@"SELECT Bit FROM Test" completion:^(NSArray* results) { XCTAssertEqualObjects(results[0][0][@"Bit"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"Bit"], @(1)); - XCTAssertEqualObjects(results[0][2][@"Bit"], @(0)); + XCTAssertEqualObjects(results[0][1][@"Bit"], @(YES)); + XCTAssertEqualObjects(results[0][2][@"Bit"], @(NO)); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; From 57497b2301e2864453667def0ea5b9d03203d545 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 28 Oct 2016 22:22:57 -0400 Subject: [PATCH 096/127] Added NSNull to type list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 01ddc44..b35985a 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ SQLClient maps SQL Server data types into the following native Objective-C types * money → NSDecimalNumber **(last 2 digits are truncated)** * nchar → NSString * ntext → NSString +* null → NSNull * numeric(p,s) → NSNumber * nvarchar → NSString * nvarchar(max) → NSString From 2a22ed3781f3be43ad6cec87e6a7acb1dfe06add Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sat, 29 Oct 2016 06:03:18 -0400 Subject: [PATCH 097/127] Changed type order --- SQLClient/SQLClient/SQLClient/SQLClient.m | 42 +++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 15e7b01..06c7e35 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -446,6 +446,13 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu value = [NSNumber numberWithFloat:_value]; break; } + case SYBDECIMAL: //Numbers from -10^38 +1 to 10^38 –1. + case SYBNUMERIC: + { + NSString* _value = [[NSString alloc] initWithUTF8String:(char*)column->data]; + value = [NSDecimalNumber decimalNumberWithString:_value]; + break; + } case SYBMONEY4: //Monetary data from -214,748.3648 to 214,748.3647 { DBMONEY4 _value; @@ -463,22 +470,6 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu free(_value); break; } - case SYBDECIMAL: //Numbers from -10^38 +1 to 10^38 –1. - case SYBNUMERIC: - { - NSString* _value = [[NSString alloc] initWithUTF8String:(char*)column->data]; - value = [NSDecimalNumber decimalNumberWithString:_value]; - break; - } - case SYBCHAR: - case SYBVARCHAR: - case SYBNVARCHAR: - case SYBTEXT: - case SYBNTEXT: - { - value = [NSString stringWithUTF8String:(char*)column->data]; - break; - } case SYBDATETIME: //From 1/1/1753 00:00:00.000 to 12/31/9999 23:59:59.997 with an accuracy of 3.33 milliseconds case SYBDATETIMN: case SYBBIGDATETIME: @@ -512,6 +503,20 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu value = [self dateWithTimeString:string]; break; } + case SYBCHAR: + case SYBVARCHAR: + case SYBNVARCHAR: + case SYBTEXT: + case SYBNTEXT: + { + value = [NSString stringWithUTF8String:(char*)column->data]; + break; + } + case SYBUNIQUEIDENTIFIER: //https://en.wikipedia.org/wiki/Globally_unique_identifier#Binary_encoding + { + value = [[NSUUID alloc] initWithUUIDBytes:column->data]; + break; + } case SYBVOID: case SYBBINARY: case SYBVARBINARY: @@ -525,11 +530,6 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu value = [UIImage imageWithData:data]; break; } - case SYBUNIQUEIDENTIFIER: //https://en.wikipedia.org/wiki/Globally_unique_identifier#Binary_encoding - { - value = [[NSUUID alloc] initWithUUIDBytes:column->data]; - break; - } } } From 7a69d3fde6e65654a3008735abfe157f21baac13 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sat, 29 Oct 2016 07:14:51 -0400 Subject: [PATCH 098/127] Query server for max text size --- SQLClient/SQLClient/SQLClient/SQLClient.h | 5 +++++ SQLClient/SQLClient/SQLClient/SQLClient.m | 13 ++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.h b/SQLClient/SQLClient/SQLClient/SQLClient.h index f1720ce..70d17d8 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.h +++ b/SQLClient/SQLClient/SQLClient/SQLClient.h @@ -64,6 +64,11 @@ extern NSString* _Nonnull const SQLClientSeverityKey; */ - (BOOL)isExecuting; +/** + * Returns the maximum text size configured on the server (default 4096). + */ +- (int)maxTextSize; + /** * Executes a SQL statement. Results of queries will be passed to the completion handler. Inserts, updates, and deletes do not return results. * diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 06c7e35..b2c1292 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -14,7 +14,7 @@ #define SYBUNIQUEIDENTIFIER 36 int const SQLClientDefaultTimeout = 5; -int const SQLClientVarcharMaxLength = 8000; //VARCHAR(N) max length +int const SQLClientDefaultMaxTextSize = 4096; NSString* const SQLClientDefaultCharset = @"UTF-8"; NSString* const SQLClientWorkerQueueName = @"com.martinrybak.sqlclient"; NSString* const SQLClientPendingConnectionError = @"Attempting to connect while a connection is active."; @@ -40,7 +40,8 @@ @interface SQLClient () @property (nonatomic, strong) NSOperationQueue* workerQueue; @property (nonatomic, weak) NSOperationQueue* callbackQueue; -@property (atomic, assign, getter=isExecuting) BOOL executing; //Atomic because can be called from public API on !self.workerQueue +@property (atomic, assign, getter=isExecuting) BOOL executing; +@property (atomic, assign) int maxTextSize; @end @@ -72,6 +73,7 @@ - (id)init self.workerQueue = [[NSOperationQueue alloc] init]; self.workerQueue.name = SQLClientWorkerQueueName; self.workerQueue.maxConcurrentOperationCount = 1; + self.maxTextSize = SQLClientDefaultMaxTextSize; self.executing = NO; //Set FreeTDS callback handlers @@ -159,6 +161,11 @@ - (void)connect:(nonnull NSString*)host //Success! [self connectionSuccess:completion]; [self cleanupAfterConnection]; + + //Query server for max text size + [self execute:@"SELECT @@TEXTSIZE AS MaxTextSize" completion:^(NSArray* results) { + self.maxTextSize = [results[0][0][@"MaxTextSize"] integerValue]; + }]; }]; } @@ -312,7 +319,7 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu case SYBNTEXT: { varType = NTBSTRINGBIND; - column->size = MIN(column->size, SQLClientVarcharMaxLength); + column->size = MIN(column->size, self.maxTextSize); break; } case SYBDATETIME: From 60f6cc0e18d25c52116cbbb89abfedb469a681b9 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sat, 29 Oct 2016 07:16:40 -0400 Subject: [PATCH 099/127] Updated readme with max text size --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b35985a..62e7d6e 100644 --- a/README.md +++ b/README.md @@ -77,17 +77,19 @@ SQLClient maps SQL Server data types into the following native Objective-C types * smallmoney → NSDecimalNumber * sql_variant → **not supported** * table → **not supported** -* text → NSString +* text → NSString* * time → **NSDate** or **NSString**† * timestamp → NSData * tinyint → NSNumber * uniqueidentifier → NSUUID * varbinary → NSData * varbinary(max) → NSData -* varchar(max) → NSString **(max length 8000)** +* varchar(max) → NSString* * varchar(n) → NSString * xml → NSString +*The maximum length of a string in a query is configured on the server via the `SET TEXTSIZE` command. SQLClient uses this length or defaults to `4096` if not available. + †The following data types are only converted to **NSDate** on TDS version **7.3** and higher. By default FreeTDS uses version **7.1** of the TDS protocol, which converts them to **NSString**. To use a higher version of the TDS protocol, add an environment variable to Xcode named `TDSVER`. Possible values are `4.2`, `5.0`, `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `auto`. A value of `auto` tells FreeTDS to use an autodetection (trial-and-error) algorithm to choose the highest available protocol version. From cc5348840a5eaba28987e48f10840544ce6ca810 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sat, 29 Oct 2016 07:33:09 -0400 Subject: [PATCH 100/127] Added string tests --- SQLClient/SQLClientTests/SQLClientTests.m | 61 +++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index 0f703ba..3cff9de 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -208,6 +208,56 @@ - (void)testTime [self waitForExpectationsWithTimeout:1000 handler:nil]; } +#pragma mark - Text + +- (void)testChar +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT Char10 FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Char10"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"Char10"], @"a"); + XCTAssertEqualObjects(results[0][2][@"Char10"], @"abcdefghi"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testVarChar +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT VarCharMax FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"VarCharMax"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"VarCharMax"], @"a"); + XCTAssertEqual([results[0][2][@"VarCharMax"] length], [SQLClient sharedInstance].maxTextSize - 1); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testText +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT Text FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Text"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"Text"], @"a"); + XCTAssertEqual([results[0][2][@"Text"] length], [SQLClient sharedInstance].maxTextSize - 1); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testXml +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT Xml FROM Test" completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Xml"], [NSNull null]); + XCTAssertEqualObjects(results[0][1][@"Xml"], @""); + XCTAssertEqual([results[0][2][@"Xml"] length], [SQLClient sharedInstance].maxTextSize - 1); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + #pragma mark - Private - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion @@ -249,4 +299,15 @@ - (NSDate*)dateWithYear:(int)year month:(int)month day:(int)day hour:(int)hour m return [calendar dateFromComponents:dateComponents]; } +- (NSString*)stringWithLength:(NSUInteger)length +{ + NSMutableString* output = [NSMutableString string]; + for (NSUInteger i = 0; i < length; i++) { + //32-127 == printable ASCII values + char character = arc4random_uniform(95) + 32; + [output appendString:[NSString stringWithFormat:@"%c", character]]; + } + return [output copy]; +} + @end From aca7b4f7c248f8d7bc3dca10ef8c7c854ec282f3 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sat, 29 Oct 2016 07:34:09 -0400 Subject: [PATCH 101/127] Updated pragmas and fixed timeout --- SQLClient/SQLClientTests/SQLClientTests.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index 3cff9de..463d7be 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -15,6 +15,8 @@ @interface SQLClientTests : XCTestCase @implementation SQLClientTests +#pragma mark - Numbers + - (void)testBit { XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; @@ -131,6 +133,8 @@ - (void)testMoney [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } +#pragma mark - Dates + - (void)testSmallDateTime { XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; @@ -155,7 +159,7 @@ - (void)testDateTime [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } -#pragma mark - TDS 7.3 +#pragma mark TDS 7.3 //If these test fail, you must tell FreeTDS to use the TDS protocol >= 7.3. //Add an environment variable to the test scheme with name TDSVER and value auto @@ -205,7 +209,7 @@ - (void)testTime XCTAssertEqualObjects(results[0][2][@"Time"], [self dateWithYear:1900 month:1 day:1 hour:23 minute:59 second:59 nanosecond:999999900 timezone:0]); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:1000 handler:nil]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } #pragma mark - Text From c54e49e1a33f3d6c5123baae007103d9a357f7f3 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Sat, 29 Oct 2016 07:34:20 -0400 Subject: [PATCH 102/127] Added placeholder tests --- SQLClient/SQLClientTests/SQLClientTests.m | 32 ++++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index 463d7be..8b717ff 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -101,10 +101,15 @@ - (void)testReal [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } -/* TODO: - decimal - numeric - */ +- (void)testDecimal +{ + +} + +- (void)testNumeric +{ + +} - (void)testSmallMoney { @@ -262,6 +267,25 @@ - (void)testXml [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } +#pragma mar - Uniqueidentifier + +- (void)testUniqueIdentifier +{ + +} + +#pragma mark - Binary + +- (void)testBinary +{ + +} + +- (void)testImage +{ + +} + #pragma mark - Private - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion From bada2542069a6ee045813eab9e89fbc486b1c619 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 31 Oct 2016 15:37:03 -0400 Subject: [PATCH 103/127] Converted tests to not require database table --- README.md | 1 - SQLClient/SQLClientTests/SQLClientTests.m | 541 +++++++++++++++------- 2 files changed, 362 insertions(+), 180 deletions(-) diff --git a/README.md b/README.md index 62e7d6e..dc0dfe9 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,6 @@ To configure the connection for your server: * `DATABASE` (optional) * `USERNAME` * `PASSWORD` -* Set up your database by running the script located in the `setup.sql` file in the `SQLClientTests` folder. ## Known Issues PR's welcome! diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index 8b717ff..e375ea2 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -15,256 +15,423 @@ @interface SQLClientTests : XCTestCase @implementation SQLClientTests -#pragma mark - Numbers +#pragma mark - Bit -- (void)testBit +- (void)testBitWithNull { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT Bit FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"Bit"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"Bit"], @(YES)); - XCTAssertEqualObjects(results[0][2][@"Bit"], @(NO)); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = [NSNull null]; + [self testServerType:@"BIT" serverValue:nil expectedValue:value]; } -- (void)testTinyInt +- (void)testBitWithTrue { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT TinyInt FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"TinyInt"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"TinyInt"], @(0)); - XCTAssertEqualObjects(results[0][2][@"TinyInt"], @(UCHAR_MAX)); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = @(YES); + [self testServerType:@"BIT" serverValue:value expectedValue:value]; } -- (void)testSmallInt +- (void)testBitWithFalse { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT SmallInt FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"SmallInt"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"SmallInt"], @(SHRT_MIN)); - XCTAssertEqualObjects(results[0][2][@"SmallInt"], @(SHRT_MAX)); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = @(YES); + [self testServerType:@"BIT" serverValue:value expectedValue:value]; } -- (void)testInt +#pragma mark - Tiny Int + +- (void)testTinyIntWithNull { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT Int FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"Int"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"Int"], @(INT_MIN)); - XCTAssertEqualObjects(results[0][2][@"Int"], @(INT_MAX)); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = [NSNull null]; + [self testServerType:@"TINYINT" serverValue:nil expectedValue:value]; } -- (void)testBigInt +- (void)testTinyIntWithMinimum { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT BigInt FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"BigInt"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"BigInt"], @(LLONG_MIN)); - XCTAssertEqualObjects(results[0][2][@"BigInt"], @(LLONG_MAX)); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = @(0); + [self testServerType:@"TINYINT" serverValue:value expectedValue:value]; } -- (void)testFloat +- (void)testTinyIntWithMaximum { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT Float FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"Float"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"Float"], @(-1.79e+308)); - XCTAssertEqualObjects(results[0][2][@"Float"], @(1.79e+308)); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = @(UCHAR_MAX); + [self testServerType:@"TINYINT" serverValue:value expectedValue:value]; } -- (void)testReal +#pragma mark - Small Int + +- (void)testSmallIntWithNull { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT Real FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"Real"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"Real"], [NSNumber numberWithFloat:-3.4e+38]); - XCTAssertEqualObjects(results[0][2][@"Real"], [NSNumber numberWithFloat:3.4e+38]); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = [NSNull null]; + [self testServerType:@"SMALLINT" serverValue:nil expectedValue:value]; +} + +- (void)testSmallIntWithMinimum +{ + id value = @(SHRT_MIN); + [self testServerType:@"SMALLINT" serverValue:value expectedValue:value]; +} + +- (void)testSmallIntWithMaximum +{ + id value = @(SHRT_MAX); + [self testServerType:@"SMALLINT" serverValue:value expectedValue:value]; } +#pragma mark - Int + +- (void)testIntWithNull +{ + id value = [NSNull null]; + [self testServerType:@"INT" serverValue:nil expectedValue:value]; +} + +- (void)testIntWithMinimum +{ + id value = @(SHRT_MIN); + [self testServerType:@"INT" serverValue:value expectedValue:value]; +} + +- (void)testIntWithMaximum +{ + id value = @(SHRT_MAX); + [self testServerType:@"INT" serverValue:value expectedValue:value]; +} + +#pragma mark - Big Int + +- (void)testBigIntWithNull +{ + id value = [NSNull null]; + [self testServerType:@"BIGINT" serverValue:nil expectedValue:value]; +} + +- (void)testBigIntWithMinimum +{ + id value = @(LLONG_MIN); + [self testServerType:@"BIGINT" serverValue:value expectedValue:value]; +} + +- (void)testBigIntWithMaximum +{ + id value = @(LLONG_MAX); + [self testServerType:@"BIGINT" serverValue:value expectedValue:value]; +} + +#pragma mark - Float + +- (void)testFloatWithNull +{ + id value = [NSNull null]; + [self testServerType:@"FLOAT" serverValue:nil expectedValue:value]; +} + +- (void)testFloatWithMinimum +{ + id value = @(-1.79e+308); + [self testServerType:@"FLOAT" serverValue:value expectedValue:value]; +} + +- (void)testFloatWithMaximum +{ + id value = @(1.79e+308); + [self testServerType:@"FLOAT" serverValue:value expectedValue:value]; +} + +#pragma mark - Real + +- (void)testRealWithNull +{ + id value = [NSNull null]; + [self testServerType:@"REAL" serverValue:nil expectedValue:value]; +} + +- (void)testRealWithMinimum +{ + id value = [NSNumber numberWithFloat:-3.4e+38]; + [self testServerType:@"REAL" serverValue:value expectedValue:value]; +} + +- (void)testRealWithMaximum +{ + id value = [NSNumber numberWithFloat:3.4e+38]; + [self testServerType:@"REAL" serverValue:value expectedValue:value]; +} + +#pragma mark - Decimal + - (void)testDecimal { } +#pragma mark - Numeric + - (void)testNumeric { } -- (void)testSmallMoney +#pragma mark - Small Money + +- (void)testSmallMoneyWithNull { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT SmallMoney FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"SmallMoney"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"SmallMoney"], [NSDecimalNumber decimalNumberWithString:@"-214748.3648"]); - XCTAssertEqualObjects(results[0][2][@"SmallMoney"], [NSDecimalNumber decimalNumberWithString:@"214748.3647"]); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = [NSNull null]; + [self testServerType:@"SMALLMONEY" serverValue:nil expectedValue:value]; } -- (void)testMoney +- (void)testSmallMoneyWithMinimum { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT Money FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"Money"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"Money"], [NSDecimalNumber decimalNumberWithString:@"-922337203685477.58"]); - XCTAssertEqualObjects(results[0][2][@"Money"], [NSDecimalNumber decimalNumberWithString:@"922337203685477.58"]); - //TODO: fix last 2 digits truncated - //XCTAssertEqualObjects(results[0][1][@"Money"], [NSDecimalNumber decimalNumberWithString:@"-922337203685477.5808"]); - //XCTAssertEqualObjects(results[0][2][@"Money"], [NSDecimalNumber decimalNumberWithString:@"922337203685477.5807"]); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = [NSDecimalNumber decimalNumberWithString:@"-214748.3648"]; + [self testServerType:@"SMALLMONEY" serverValue:value expectedValue:value]; +} + +- (void)testSmallMoneyWithMaximum +{ + id value = [NSDecimalNumber decimalNumberWithString:@"214748.3647"]; + [self testServerType:@"SMALLMONEY" serverValue:value expectedValue:value]; } -#pragma mark - Dates +#pragma mark - Money + +//TODO: fix last 2 digits truncated +//[NSDecimalNumber decimalNumberWithString:@"-922337203685477.5808"]; +//[NSDecimalNumber decimalNumberWithString:@"922337203685477.5807"]; -- (void)testSmallDateTime +- (void)testMoneyWithNull { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT SmallDateTime FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"SmallDateTime"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"SmallDateTime"], [self dateWithYear:1900 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]); - XCTAssertEqualObjects(results[0][2][@"SmallDateTime"], [self dateWithYear:2079 month:6 day:6 hour:23 minute:59 second:0 nanosecond:0 timezone:0]); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = [NSNull null]; + [self testServerType:@"MONEY" serverValue:nil expectedValue:value]; } -- (void)testDateTime +- (void)testMoneyWithMinimum { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT DateTime FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"DateTime"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"DateTime"], [self dateWithYear:1753 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]); - XCTAssertEqualObjects(results[0][2][@"DateTime"], [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:997000000 timezone:0]); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = [NSDecimalNumber decimalNumberWithString:@"-922337203685477.58"]; + [self testServerType:@"MONEY" serverValue:value expectedValue:value]; } -#pragma mark TDS 7.3 +- (void)testMoneyWithMaximum +{ + id value = [NSDecimalNumber decimalNumberWithString:@"922337203685477.58"]; + [self testServerType:@"MONEY" serverValue:value expectedValue:value]; +} + +#pragma mark - Small DateTime + +- (void)testSmallDateTimeWithNull +{ + id value = [NSNull null]; + [self testServerType:@"SMALLDATETIME" serverValue:nil expectedValue:value]; +} + +- (void)testSmallDateTimeWithMinimum +{ + id value = @"01-01-1900 00:00:00"; + id expectedValue = [self dateWithYear:1900 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; + [self testServerType:@"SMALLDATETIME" serverValue:value expectedValue:expectedValue]; +} + +- (void)testSmallDateTimeWithMaximum +{ + id value = @"06-06-2079 23:59:00"; + id expectedValue = [self dateWithYear:2079 month:6 day:6 hour:23 minute:59 second:0 nanosecond:0 timezone:0]; + [self testServerType:@"SMALLDATETIME" serverValue:value expectedValue:expectedValue]; +} + +#pragma mark - DateTime + +- (void)testDateTimeWithNull +{ + id value = [NSNull null]; + [self testServerType:@"DATETIME" serverValue:nil expectedValue:value]; +} + +- (void)testDateTimeWithMinimum +{ + id value = @"01-01-1753 00:00:00:000"; + id expectedValue = [self dateWithYear:1753 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; + [self testServerType:@"DATETIME" serverValue:value expectedValue:expectedValue]; +} + +- (void)testDateTimeWithMaximum +{ + id value = @"12-31-9999 23:59:59:997"; + id expectedValue = [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:997000000 timezone:0]; + [self testServerType:@"DATETIME" serverValue:value expectedValue:expectedValue]; +} + +#pragma mark - DateTime2 //If these test fail, you must tell FreeTDS to use the TDS protocol >= 7.3. //Add an environment variable to the test scheme with name TDSVER and value auto -- (void)testDateTime2 +- (void)testDateTime2WithNull { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT DateTime2 FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"DateTime2"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"DateTime2"], [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]); - XCTAssertEqualObjects(results[0][2][@"DateTime2"], [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:999999900 timezone:0]); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = [NSNull null]; + [self testServerType:@"DATETIME2" serverValue:nil expectedValue:value]; } -- (void)testDateTimeOffset +- (void)testDateTime2WithMinimum { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT DateTimeOffset FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"DateTimeOffset"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"DateTimeOffset"], [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:-840]); - XCTAssertEqualObjects(results[0][2][@"DateTimeOffset"], [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:999999900 timezone:840]); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = @"01-01-0001 00:00:00.0000000"; + id expectedValue = [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; + [self testServerType:@"DATETIME2" serverValue:value expectedValue:expectedValue]; } -- (void)testDate +- (void)testDateTime2WithMaximum { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT Date FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"Date"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"Date"], [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]); - XCTAssertEqualObjects(results[0][2][@"Date"], [self dateWithYear:9999 month:12 day:31 hour:0 minute:0 second:0 nanosecond:0 timezone:0]); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = @"12-31-9999 23:59:59.9999999"; + id expectedValue = [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:999999900 timezone:0]; + [self testServerType:@"DATETIME2" serverValue:value expectedValue:expectedValue]; +} + +#pragma mark - DateTimeOffset + +//If these test fail, you must tell FreeTDS to use the TDS protocol >= 7.3. +//Add an environment variable to the test scheme with name TDSVER and value auto + +- (void)testDateTimeOffsetWithNull +{ + id value = [NSNull null]; + [self testServerType:@"DATETIMEOFFSET" serverValue:nil expectedValue:value]; } -- (void)testTime +- (void)testDateTimeOffsetWithMinimum { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT Time FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"Time"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"Time"], [self dateWithYear:1900 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]); - XCTAssertEqualObjects(results[0][2][@"Time"], [self dateWithYear:1900 month:1 day:1 hour:23 minute:59 second:59 nanosecond:999999900 timezone:0]); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = @"01-01-0001 00:00:00.0000000 -14:00"; + id expectedValue = [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:-840]; + [self testServerType:@"DATETIMEOFFSET" serverValue:value expectedValue:expectedValue]; +} + +- (void)testDateTimeOffsetWithMaximum +{ + id value = @"12-31-9999 23:59:59.9999999 +14:00"; + id expectedValue = [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:999999900 timezone:840]; + [self testServerType:@"DATETIMEOFFSET" serverValue:value expectedValue:expectedValue]; +} + +#pragma mark - Date + +//If these test fail, you must tell FreeTDS to use the TDS protocol >= 7.3. +//Add an environment variable to the test scheme with name TDSVER and value auto + +- (void)testDateWithNull +{ + id value = [NSNull null]; + [self testServerType:@"DATE" serverValue:nil expectedValue:value]; +} + +- (void)testDateWithMinimum +{ + id value = @"01-01-0001"; + id expectedValue = [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; + [self testServerType:@"DATE" serverValue:value expectedValue:expectedValue]; +} + +- (void)testDateWithMaximum +{ + id value = @"12-31-9999"; + id expectedValue = [self dateWithYear:9999 month:12 day:31 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; + [self testServerType:@"DATE" serverValue:value expectedValue:expectedValue]; +} + +#pragma mark - Time + +- (void)testTimeWithNull +{ + id value = [NSNull null]; + [self testServerType:@"TIME" serverValue:nil expectedValue:value]; +} + +- (void)testTimeWithMinimum +{ + id value = @"00:00:00.0000000"; + id expectedValue = [self dateWithYear:1900 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; + [self testServerType:@"TIME" serverValue:value expectedValue:expectedValue]; +} + +- (void)testTimeWithMaximum +{ + id value = @"23:59:59.9999999"; + id expectedValue = [self dateWithYear:1900 month:1 day:1 hour:23 minute:59 second:59 nanosecond:999999900 timezone:0]; + [self testServerType:@"TIME" serverValue:value expectedValue:expectedValue]; +} + +#pragma mark - Char + +- (void)testCharWithNull +{ + id value = [NSNull null]; + [self testServerType:@"CHAR(1)" serverValue:nil expectedValue:value]; +} + +- (void)testCharWithMinimum +{ + //33 = minimum ASCII value (32 doesn't work) + id value = [NSString stringWithFormat:@"%c", 33]; + [self testServerType:@"CHAR(1)" serverValue:value expectedValue:value]; +} + +- (void)testCharWithMaximum +{ + //127 = maximum printable ASCII value + id value = [NSString stringWithFormat:@"%c", 127]; + [self testServerType:@"CHAR(1)" serverValue:value expectedValue:value]; +} + +#pragma mark - VarChar(Max) + +- (void)testVarCharMaxWithNull +{ + id value = [NSNull null]; + [self testServerType:@"VARCHAR(MAX)" serverValue:nil expectedValue:value]; +} + +- (void)testVarCharMaxWithMinimum +{ + //33 = minimum ASCII value (32 doesn't work) + id value = [NSString stringWithFormat:@"%c", 33]; + [self testServerType:@"VARCHAR(MAX)" serverValue:value expectedValue:value]; +} + +- (void)testVarCharMaxWithMaximum +{ + id value = [self stringWithLength:[SQLClient sharedInstance].maxTextSize + 1]; + id expectedValue = [value substringToIndex:[SQLClient sharedInstance].maxTextSize - 1]; + [self testServerType:@"VARCHAR(MAX)" serverValue:value expectedValue:expectedValue]; } #pragma mark - Text -- (void)testChar +- (void)testTextWithNull { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT Char10 FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"Char10"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"Char10"], @"a"); - XCTAssertEqualObjects(results[0][2][@"Char10"], @"abcdefghi"); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = [NSNull null]; + [self testServerType:@"TEXT" serverValue:nil expectedValue:value]; } -- (void)testVarChar +- (void)testTextWithMinimum { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT VarCharMax FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"VarCharMax"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"VarCharMax"], @"a"); - XCTAssertEqual([results[0][2][@"VarCharMax"] length], [SQLClient sharedInstance].maxTextSize - 1); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + //33 = minimum ASCII value (32 doesn't work) + id value = [NSString stringWithFormat:@"%c", 33]; + [self testServerType:@"TEXT" serverValue:value expectedValue:value]; } -- (void)testText +- (void)testTextWithMaximum { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT Text FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"Text"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"Text"], @"a"); - XCTAssertEqual([results[0][2][@"Text"] length], [SQLClient sharedInstance].maxTextSize - 1); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = [self stringWithLength:[SQLClient sharedInstance].maxTextSize + 1]; + id expectedValue = [value substringToIndex:[SQLClient sharedInstance].maxTextSize - 1]; + [self testServerType:@"TEXT" serverValue:value expectedValue:expectedValue]; } +#pragma mark - Xml + - (void)testXml { - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:@"SELECT Xml FROM Test" completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"Xml"], [NSNull null]); - XCTAssertEqualObjects(results[0][1][@"Xml"], @""); - XCTAssertEqual([results[0][2][@"Xml"] length], [SQLClient sharedInstance].maxTextSize - 1); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +// XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; +// [self execute:@"SELECT Xml FROM Test" completion:^(NSArray* results) { +// XCTAssertEqualObjects(results[0][0][@"Xml"], [NSNull null]); +// XCTAssertEqualObjects(results[0][1][@"Xml"], @""); +// XCTAssertEqual([results[0][2][@"Xml"] length], [SQLClient sharedInstance].maxTextSize - 1); +// [expectation fulfill]; +// }]; +// [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } #pragma mar - Uniqueidentifier @@ -288,6 +455,21 @@ - (void)testImage #pragma mark - Private +- (void)testServerType:(NSString*)serverType serverValue:(id)serverValue expectedValue:(id)expectedValue +{ + NSString* sql = [NSString stringWithFormat:@"SELECT CAST(NULL AS %@) AS Value", serverType]; + if (serverValue) { + sql = [NSString stringWithFormat:@"SELECT CAST('%@' AS %@) AS Value", serverValue, serverType]; + } + + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:sql completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Value"], expectedValue); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + - (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion { //Environment variables from the Test Debug Scheme @@ -331,11 +513,12 @@ - (NSString*)stringWithLength:(NSUInteger)length { NSMutableString* output = [NSMutableString string]; for (NSUInteger i = 0; i < length; i++) { - //32-127 == printable ASCII values - char character = arc4random_uniform(95) + 32; + //65-122 == alphanumeric ASCII values + char character = arc4random_uniform(65) + 57; [output appendString:[NSString stringWithFormat:@"%c", character]]; } - return [output copy]; + //Sanitize + return [output stringByReplacingOccurrencesOfString:@"'" withString:@"''"]; } @end From 21c96a7ac398ba758d7165bc6853c4f6e4c7efa7 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 31 Oct 2016 15:56:43 -0400 Subject: [PATCH 104/127] Fixed GUID to UUID conversion --- SQLClient/SQLClient/SQLClient/SQLClient.m | 15 ++++++++++++++- SQLClient/SQLClientTests/SQLClientTests.m | 12 ++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index b2c1292..91b6ad6 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -27,6 +27,13 @@ NSString* const SQLClientCodeKey = @"SQLClientCodeKey"; NSString* const SQLClientSeverityKey = @"SQLClientSeverityKey"; +struct GUID { + unsigned long data1; + unsigned short data2; + unsigned short data3; + unsigned char data4[8]; +}; + struct COLUMN { char* name; @@ -521,7 +528,13 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu } case SYBUNIQUEIDENTIFIER: //https://en.wikipedia.org/wiki/Globally_unique_identifier#Binary_encoding { - value = [[NSUUID alloc] initWithUUIDBytes:column->data]; + //Convert GUID to UUID + struct GUID _value; + memcpy(&_value, column->data, sizeof _value); + _value.data1 = NTOHL(_value.data1); + _value.data2 = NTOHS(_value.data2); + _value.data3 = NTOHS(_value.data3); + value = [[NSUUID alloc] initWithUUIDBytes:(const unsigned char*)&_value]; break; } case SYBVOID: diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index e375ea2..3659a5d 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -436,9 +436,17 @@ - (void)testXml #pragma mar - Uniqueidentifier -- (void)testUniqueIdentifier +- (void)testUniqueIdentifierWithNull { - + id value = [NSNull null]; + [self testServerType:@"UNIQUEIDENTIFIER" serverValue:nil expectedValue:value]; +} + +- (void)testUniqueIdentifierWithValue +{ + id expectedValue = [NSUUID UUID]; + id value = [expectedValue UUIDString]; + [self testServerType:@"UNIQUEIDENTIFIER" serverValue:value expectedValue:expectedValue]; } #pragma mark - Binary From b594352e8b752d3505d40ef515d49e08c4797c97 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 31 Oct 2016 15:56:52 -0400 Subject: [PATCH 105/127] Updated comments --- SQLClient/SQLClientTests/SQLClientTests.m | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index 3659a5d..db9f611 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -191,10 +191,6 @@ - (void)testSmallMoneyWithMaximum #pragma mark - Money -//TODO: fix last 2 digits truncated -//[NSDecimalNumber decimalNumberWithString:@"-922337203685477.5808"]; -//[NSDecimalNumber decimalNumberWithString:@"922337203685477.5807"]; - - (void)testMoneyWithNull { id value = [NSNull null]; @@ -203,12 +199,14 @@ - (void)testMoneyWithNull - (void)testMoneyWithMinimum { + //TODO: fix last 2 digits, i.e. -922337203685477.5808 returns -922337203685477.58 id value = [NSDecimalNumber decimalNumberWithString:@"-922337203685477.58"]; [self testServerType:@"MONEY" serverValue:value expectedValue:value]; } - (void)testMoneyWithMaximum { + //TODO: fix last 2 digits, i.e. 922337203685477.5807 returns 922337203685477.58 id value = [NSDecimalNumber decimalNumberWithString:@"922337203685477.58"]; [self testServerType:@"MONEY" serverValue:value expectedValue:value]; } From f4951f164335ab4943769839e25d035d839e9ebc Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Mon, 31 Oct 2016 19:21:02 -0400 Subject: [PATCH 106/127] Added binary and image tests --- SQLClient/SQLClientTests/SQLClientTests.m | 233 +++++++++++++--------- 1 file changed, 135 insertions(+), 98 deletions(-) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index db9f611..d0bd21d 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -20,19 +20,19 @@ @implementation SQLClientTests - (void)testBitWithNull { id value = [NSNull null]; - [self testServerType:@"BIT" serverValue:nil expectedValue:value]; + [self testCast:@"BIT" input:nil output:value]; } - (void)testBitWithTrue { id value = @(YES); - [self testServerType:@"BIT" serverValue:value expectedValue:value]; + [self testCast:@"BIT" input:value output:value]; } - (void)testBitWithFalse { id value = @(YES); - [self testServerType:@"BIT" serverValue:value expectedValue:value]; + [self testCast:@"BIT" input:value output:value]; } #pragma mark - Tiny Int @@ -40,19 +40,19 @@ - (void)testBitWithFalse - (void)testTinyIntWithNull { id value = [NSNull null]; - [self testServerType:@"TINYINT" serverValue:nil expectedValue:value]; + [self testCast:@"TINYINT" input:nil output:value]; } - (void)testTinyIntWithMinimum { id value = @(0); - [self testServerType:@"TINYINT" serverValue:value expectedValue:value]; + [self testCast:@"TINYINT" input:value output:value]; } - (void)testTinyIntWithMaximum { id value = @(UCHAR_MAX); - [self testServerType:@"TINYINT" serverValue:value expectedValue:value]; + [self testCast:@"TINYINT" input:value output:value]; } #pragma mark - Small Int @@ -60,19 +60,19 @@ - (void)testTinyIntWithMaximum - (void)testSmallIntWithNull { id value = [NSNull null]; - [self testServerType:@"SMALLINT" serverValue:nil expectedValue:value]; + [self testCast:@"SMALLINT" input:nil output:value]; } - (void)testSmallIntWithMinimum { id value = @(SHRT_MIN); - [self testServerType:@"SMALLINT" serverValue:value expectedValue:value]; + [self testCast:@"SMALLINT" input:value output:value]; } - (void)testSmallIntWithMaximum { id value = @(SHRT_MAX); - [self testServerType:@"SMALLINT" serverValue:value expectedValue:value]; + [self testCast:@"SMALLINT" input:value output:value]; } #pragma mark - Int @@ -80,19 +80,19 @@ - (void)testSmallIntWithMaximum - (void)testIntWithNull { id value = [NSNull null]; - [self testServerType:@"INT" serverValue:nil expectedValue:value]; + [self testCast:@"INT" input:nil output:value]; } - (void)testIntWithMinimum { id value = @(SHRT_MIN); - [self testServerType:@"INT" serverValue:value expectedValue:value]; + [self testCast:@"INT" input:value output:value]; } - (void)testIntWithMaximum { id value = @(SHRT_MAX); - [self testServerType:@"INT" serverValue:value expectedValue:value]; + [self testCast:@"INT" input:value output:value]; } #pragma mark - Big Int @@ -100,19 +100,19 @@ - (void)testIntWithMaximum - (void)testBigIntWithNull { id value = [NSNull null]; - [self testServerType:@"BIGINT" serverValue:nil expectedValue:value]; + [self testCast:@"BIGINT" input:nil output:value]; } - (void)testBigIntWithMinimum { id value = @(LLONG_MIN); - [self testServerType:@"BIGINT" serverValue:value expectedValue:value]; + [self testCast:@"BIGINT" input:value output:value]; } - (void)testBigIntWithMaximum { id value = @(LLONG_MAX); - [self testServerType:@"BIGINT" serverValue:value expectedValue:value]; + [self testCast:@"BIGINT" input:value output:value]; } #pragma mark - Float @@ -120,19 +120,19 @@ - (void)testBigIntWithMaximum - (void)testFloatWithNull { id value = [NSNull null]; - [self testServerType:@"FLOAT" serverValue:nil expectedValue:value]; + [self testCast:@"FLOAT" input:nil output:value]; } - (void)testFloatWithMinimum { id value = @(-1.79e+308); - [self testServerType:@"FLOAT" serverValue:value expectedValue:value]; + [self testCast:@"FLOAT" input:value output:value]; } - (void)testFloatWithMaximum { id value = @(1.79e+308); - [self testServerType:@"FLOAT" serverValue:value expectedValue:value]; + [self testCast:@"FLOAT" input:value output:value]; } #pragma mark - Real @@ -140,19 +140,19 @@ - (void)testFloatWithMaximum - (void)testRealWithNull { id value = [NSNull null]; - [self testServerType:@"REAL" serverValue:nil expectedValue:value]; + [self testCast:@"REAL" input:nil output:value]; } - (void)testRealWithMinimum { id value = [NSNumber numberWithFloat:-3.4e+38]; - [self testServerType:@"REAL" serverValue:value expectedValue:value]; + [self testCast:@"REAL" input:value output:value]; } - (void)testRealWithMaximum { id value = [NSNumber numberWithFloat:3.4e+38]; - [self testServerType:@"REAL" serverValue:value expectedValue:value]; + [self testCast:@"REAL" input:value output:value]; } #pragma mark - Decimal @@ -174,19 +174,19 @@ - (void)testNumeric - (void)testSmallMoneyWithNull { id value = [NSNull null]; - [self testServerType:@"SMALLMONEY" serverValue:nil expectedValue:value]; + [self testCast:@"SMALLMONEY" input:nil output:value]; } - (void)testSmallMoneyWithMinimum { id value = [NSDecimalNumber decimalNumberWithString:@"-214748.3648"]; - [self testServerType:@"SMALLMONEY" serverValue:value expectedValue:value]; + [self testCast:@"SMALLMONEY" input:value output:value]; } - (void)testSmallMoneyWithMaximum { id value = [NSDecimalNumber decimalNumberWithString:@"214748.3647"]; - [self testServerType:@"SMALLMONEY" serverValue:value expectedValue:value]; + [self testCast:@"SMALLMONEY" input:value output:value]; } #pragma mark - Money @@ -194,21 +194,21 @@ - (void)testSmallMoneyWithMaximum - (void)testMoneyWithNull { id value = [NSNull null]; - [self testServerType:@"MONEY" serverValue:nil expectedValue:value]; + [self testCast:@"MONEY" input:nil output:value]; } - (void)testMoneyWithMinimum { //TODO: fix last 2 digits, i.e. -922337203685477.5808 returns -922337203685477.58 id value = [NSDecimalNumber decimalNumberWithString:@"-922337203685477.58"]; - [self testServerType:@"MONEY" serverValue:value expectedValue:value]; + [self testCast:@"MONEY" input:value output:value]; } - (void)testMoneyWithMaximum { //TODO: fix last 2 digits, i.e. 922337203685477.5807 returns 922337203685477.58 id value = [NSDecimalNumber decimalNumberWithString:@"922337203685477.58"]; - [self testServerType:@"MONEY" serverValue:value expectedValue:value]; + [self testCast:@"MONEY" input:value output:value]; } #pragma mark - Small DateTime @@ -216,21 +216,21 @@ - (void)testMoneyWithMaximum - (void)testSmallDateTimeWithNull { id value = [NSNull null]; - [self testServerType:@"SMALLDATETIME" serverValue:nil expectedValue:value]; + [self testCast:@"SMALLDATETIME" input:nil output:value]; } - (void)testSmallDateTimeWithMinimum { id value = @"01-01-1900 00:00:00"; - id expectedValue = [self dateWithYear:1900 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; - [self testServerType:@"SMALLDATETIME" serverValue:value expectedValue:expectedValue]; + id output = [self dateWithYear:1900 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; + [self testCast:@"SMALLDATETIME" input:value output:output]; } - (void)testSmallDateTimeWithMaximum { id value = @"06-06-2079 23:59:00"; - id expectedValue = [self dateWithYear:2079 month:6 day:6 hour:23 minute:59 second:0 nanosecond:0 timezone:0]; - [self testServerType:@"SMALLDATETIME" serverValue:value expectedValue:expectedValue]; + id output = [self dateWithYear:2079 month:6 day:6 hour:23 minute:59 second:0 nanosecond:0 timezone:0]; + [self testCast:@"SMALLDATETIME" input:value output:output]; } #pragma mark - DateTime @@ -238,118 +238,121 @@ - (void)testSmallDateTimeWithMaximum - (void)testDateTimeWithNull { id value = [NSNull null]; - [self testServerType:@"DATETIME" serverValue:nil expectedValue:value]; + [self testCast:@"DATETIME" input:nil output:value]; } - (void)testDateTimeWithMinimum { id value = @"01-01-1753 00:00:00:000"; - id expectedValue = [self dateWithYear:1753 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; - [self testServerType:@"DATETIME" serverValue:value expectedValue:expectedValue]; + id output = [self dateWithYear:1753 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; + [self testCast:@"DATETIME" input:value output:output]; } - (void)testDateTimeWithMaximum { id value = @"12-31-9999 23:59:59:997"; - id expectedValue = [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:997000000 timezone:0]; - [self testServerType:@"DATETIME" serverValue:value expectedValue:expectedValue]; + id output = [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:997000000 timezone:0]; + [self testCast:@"DATETIME" input:value output:output]; } #pragma mark - DateTime2 -//If these test fail, you must tell FreeTDS to use the TDS protocol >= 7.3. +//If these tests fail, you must tell FreeTDS to use the TDS protocol >= 7.3. //Add an environment variable to the test scheme with name TDSVER and value auto - (void)testDateTime2WithNull { id value = [NSNull null]; - [self testServerType:@"DATETIME2" serverValue:nil expectedValue:value]; + [self testCast:@"DATETIME2" input:nil output:value]; } - (void)testDateTime2WithMinimum { id value = @"01-01-0001 00:00:00.0000000"; - id expectedValue = [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; - [self testServerType:@"DATETIME2" serverValue:value expectedValue:expectedValue]; + id output = [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; + [self testCast:@"DATETIME2" input:value output:output]; } - (void)testDateTime2WithMaximum { id value = @"12-31-9999 23:59:59.9999999"; - id expectedValue = [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:999999900 timezone:0]; - [self testServerType:@"DATETIME2" serverValue:value expectedValue:expectedValue]; + id output = [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:999999900 timezone:0]; + [self testCast:@"DATETIME2" input:value output:output]; } #pragma mark - DateTimeOffset -//If these test fail, you must tell FreeTDS to use the TDS protocol >= 7.3. +//If these tests fail, you must tell FreeTDS to use the TDS protocol >= 7.3. //Add an environment variable to the test scheme with name TDSVER and value auto - (void)testDateTimeOffsetWithNull { id value = [NSNull null]; - [self testServerType:@"DATETIMEOFFSET" serverValue:nil expectedValue:value]; + [self testCast:@"DATETIMEOFFSET" input:nil output:value]; } - (void)testDateTimeOffsetWithMinimum { id value = @"01-01-0001 00:00:00.0000000 -14:00"; - id expectedValue = [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:-840]; - [self testServerType:@"DATETIMEOFFSET" serverValue:value expectedValue:expectedValue]; + id output = [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:-840]; + [self testCast:@"DATETIMEOFFSET" input:value output:output]; } - (void)testDateTimeOffsetWithMaximum { id value = @"12-31-9999 23:59:59.9999999 +14:00"; - id expectedValue = [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:999999900 timezone:840]; - [self testServerType:@"DATETIMEOFFSET" serverValue:value expectedValue:expectedValue]; + id output = [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:999999900 timezone:840]; + [self testCast:@"DATETIMEOFFSET" input:value output:output]; } #pragma mark - Date -//If these test fail, you must tell FreeTDS to use the TDS protocol >= 7.3. +//If these tests fail, you must tell FreeTDS to use the TDS protocol >= 7.3. //Add an environment variable to the test scheme with name TDSVER and value auto - (void)testDateWithNull { id value = [NSNull null]; - [self testServerType:@"DATE" serverValue:nil expectedValue:value]; + [self testCast:@"DATE" input:nil output:value]; } - (void)testDateWithMinimum { id value = @"01-01-0001"; - id expectedValue = [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; - [self testServerType:@"DATE" serverValue:value expectedValue:expectedValue]; + id output = [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; + [self testCast:@"DATE" input:value output:output]; } - (void)testDateWithMaximum { id value = @"12-31-9999"; - id expectedValue = [self dateWithYear:9999 month:12 day:31 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; - [self testServerType:@"DATE" serverValue:value expectedValue:expectedValue]; + id output = [self dateWithYear:9999 month:12 day:31 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; + [self testCast:@"DATE" input:value output:output]; } #pragma mark - Time +//If these tests fail, you must tell FreeTDS to use the TDS protocol >= 7.3. +//Add an environment variable to the test scheme with name TDSVER and value auto + - (void)testTimeWithNull { id value = [NSNull null]; - [self testServerType:@"TIME" serverValue:nil expectedValue:value]; + [self testCast:@"TIME" input:nil output:value]; } - (void)testTimeWithMinimum { id value = @"00:00:00.0000000"; - id expectedValue = [self dateWithYear:1900 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; - [self testServerType:@"TIME" serverValue:value expectedValue:expectedValue]; + id output = [self dateWithYear:1900 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; + [self testCast:@"TIME" input:value output:output]; } - (void)testTimeWithMaximum { id value = @"23:59:59.9999999"; - id expectedValue = [self dateWithYear:1900 month:1 day:1 hour:23 minute:59 second:59 nanosecond:999999900 timezone:0]; - [self testServerType:@"TIME" serverValue:value expectedValue:expectedValue]; + id output = [self dateWithYear:1900 month:1 day:1 hour:23 minute:59 second:59 nanosecond:999999900 timezone:0]; + [self testCast:@"TIME" input:value output:output]; } #pragma mark - Char @@ -357,21 +360,21 @@ - (void)testTimeWithMaximum - (void)testCharWithNull { id value = [NSNull null]; - [self testServerType:@"CHAR(1)" serverValue:nil expectedValue:value]; + [self testCast:@"CHAR(1)" input:nil output:value]; } - (void)testCharWithMinimum { //33 = minimum ASCII value (32 doesn't work) id value = [NSString stringWithFormat:@"%c", 33]; - [self testServerType:@"CHAR(1)" serverValue:value expectedValue:value]; + [self testCast:@"CHAR(1)" input:value output:value]; } - (void)testCharWithMaximum { //127 = maximum printable ASCII value id value = [NSString stringWithFormat:@"%c", 127]; - [self testServerType:@"CHAR(1)" serverValue:value expectedValue:value]; + [self testCast:@"CHAR(1)" input:value output:value]; } #pragma mark - VarChar(Max) @@ -379,21 +382,21 @@ - (void)testCharWithMaximum - (void)testVarCharMaxWithNull { id value = [NSNull null]; - [self testServerType:@"VARCHAR(MAX)" serverValue:nil expectedValue:value]; + [self testCast:@"VARCHAR(MAX)" input:nil output:value]; } - (void)testVarCharMaxWithMinimum { //33 = minimum ASCII value (32 doesn't work) id value = [NSString stringWithFormat:@"%c", 33]; - [self testServerType:@"VARCHAR(MAX)" serverValue:value expectedValue:value]; + [self testCast:@"VARCHAR(MAX)" input:value output:value]; } - (void)testVarCharMaxWithMaximum { id value = [self stringWithLength:[SQLClient sharedInstance].maxTextSize + 1]; - id expectedValue = [value substringToIndex:[SQLClient sharedInstance].maxTextSize - 1]; - [self testServerType:@"VARCHAR(MAX)" serverValue:value expectedValue:expectedValue]; + id output = [value substringToIndex:[SQLClient sharedInstance].maxTextSize - 1]; + [self testCast:@"VARCHAR(MAX)" input:value output:output]; } #pragma mark - Text @@ -401,76 +404,96 @@ - (void)testVarCharMaxWithMaximum - (void)testTextWithNull { id value = [NSNull null]; - [self testServerType:@"TEXT" serverValue:nil expectedValue:value]; + [self testCast:@"TEXT" input:nil output:value]; } - (void)testTextWithMinimum { //33 = minimum ASCII value (32 doesn't work) id value = [NSString stringWithFormat:@"%c", 33]; - [self testServerType:@"TEXT" serverValue:value expectedValue:value]; + [self testCast:@"TEXT" input:value output:value]; } - (void)testTextWithMaximum { id value = [self stringWithLength:[SQLClient sharedInstance].maxTextSize + 1]; - id expectedValue = [value substringToIndex:[SQLClient sharedInstance].maxTextSize - 1]; - [self testServerType:@"TEXT" serverValue:value expectedValue:expectedValue]; + id output = [value substringToIndex:[SQLClient sharedInstance].maxTextSize - 1]; + [self testCast:@"TEXT" input:value output:output]; } -#pragma mark - Xml +#pragma mark - UniqueIdentifier -- (void)testXml +- (void)testUniqueIdentifierWithNull { -// XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; -// [self execute:@"SELECT Xml FROM Test" completion:^(NSArray* results) { -// XCTAssertEqualObjects(results[0][0][@"Xml"], [NSNull null]); -// XCTAssertEqualObjects(results[0][1][@"Xml"], @""); -// XCTAssertEqual([results[0][2][@"Xml"] length], [SQLClient sharedInstance].maxTextSize - 1); -// [expectation fulfill]; -// }]; -// [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; + id value = [NSNull null]; + [self testCast:@"UNIQUEIDENTIFIER" input:nil output:value]; } -#pragma mar - Uniqueidentifier +- (void)testUniqueIdentifierWithValue +{ + id output = [NSUUID UUID]; + id value = [output UUIDString]; + [self testCast:@"UNIQUEIDENTIFIER" input:value output:output]; +} -- (void)testUniqueIdentifierWithNull +#pragma mark - Binary + +- (void)testBinaryWithNull { id value = [NSNull null]; - [self testServerType:@"UNIQUEIDENTIFIER" serverValue:nil expectedValue:value]; + [self testCast:@"BINARY" input:nil output:value]; } -- (void)testUniqueIdentifierWithValue +- (void)testBinaryWithValue { - id expectedValue = [NSUUID UUID]; - id value = [expectedValue UUIDString]; - [self testServerType:@"UNIQUEIDENTIFIER" serverValue:value expectedValue:expectedValue]; + NSString* string = [self stringWithLength:30]; + NSData* data = [string dataUsingEncoding:NSASCIIStringEncoding]; + NSString* hex = [self hexStringWithData:data]; + [self testConvert:@"BINARY" input:hex length:1 output:data]; } -#pragma mark - Binary +#pragma mark - Image -- (void)testBinary +- (void)testImageWithNull { - + id value = [NSNull null]; + [self testCast:@"IMAGE" input:nil output:value]; } -- (void)testImage +- (void)testImageWithValue { - + UIImage* image = [UIImage imageNamed:@"AppIcon40x40"]; + NSData* data = UIImagePNGRepresentation(image); + NSString* hex = [self hexStringWithData:data]; + [self testConvert:@"IMAGE" input:hex length:1 output:data]; } #pragma mark - Private -- (void)testServerType:(NSString*)serverType serverValue:(id)serverValue expectedValue:(id)expectedValue +- (void)testCast:(NSString*)serverType input:(id)input output:(id)output { - NSString* sql = [NSString stringWithFormat:@"SELECT CAST(NULL AS %@) AS Value", serverType]; - if (serverValue) { - sql = [NSString stringWithFormat:@"SELECT CAST('%@' AS %@) AS Value", serverValue, serverType]; + NSString* sql; + if (input) { + sql = [NSString stringWithFormat:@"SELECT CAST('%@' AS %@) AS Value", input, serverType]; + } else { + sql = [NSString stringWithFormat:@"SELECT CAST(NULL AS %@) AS Value", serverType]; } XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"Value"], expectedValue); + XCTAssertEqualObjects(results[0][0][@"Value"], output); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testConvert:(NSString*)serverType input:(id)input length:(int)length output:(id)output +{ + NSString* sql = [NSString stringWithFormat:@"SELECT CONVERT(%@, '%@', %d) AS Value", serverType, input, length]; + + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:sql completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Value"], output); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; @@ -527,4 +550,18 @@ - (NSString*)stringWithLength:(NSUInteger)length return [output stringByReplacingOccurrencesOfString:@"'" withString:@"''"]; } +- (NSString*)hexStringWithData:(NSData*)data +{ + const unsigned char* dataBuffer = (const unsigned char*)[data bytes]; + if (!dataBuffer) { + return [NSString string]; + } + + NSMutableString* output = [NSMutableString stringWithCapacity:(data.length * 2)]; + for (int i = 0; i < data.length; ++i) { + [output appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]]; + } + return [NSString stringWithFormat:@"0x%@", output]; +} + @end From 357938045b04a21d5247d167f5a0ed9025e8fe28 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 2 Nov 2016 16:31:10 -0400 Subject: [PATCH 107/127] Removed image conversion. Added varbinary test --- README.md | 2 +- SQLClient/SQLClient/SQLClient/SQLClient.m | 9 ++------- SQLClient/SQLClientTests/SQLClientTests.m | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index dc0dfe9..4b0f073 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ SQLClient maps SQL Server data types into the following native Objective-C types * datetimeoffset → **NSDate** or **NSString**† * decimal(p,s) → NSNumber * float(n) → NSNumber -* image → UIImage +* image → **not supported** * int → NSNumber * money → NSDecimalNumber **(last 2 digits are truncated)** * nchar → NSString diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 91b6ad6..3ad52a3 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -359,9 +359,9 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu break; } case SYBVOID: + case SYBIMAGE: case SYBBINARY: case SYBVARBINARY: - case SYBIMAGE: case SYBUNIQUEIDENTIFIER: { varType = BINARYBIND; @@ -538,18 +538,13 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu break; } case SYBVOID: + case SYBIMAGE: case SYBBINARY: case SYBVARBINARY: { value = [NSData dataWithBytes:column->data length:column->size]; break; } - case SYBIMAGE: - { - NSData* data = [NSData dataWithBytes:column->data length:column->size]; - value = [UIImage imageWithData:data]; - break; - } } } diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index d0bd21d..46ad28b 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -449,23 +449,23 @@ - (void)testBinaryWithValue NSString* string = [self stringWithLength:30]; NSData* data = [string dataUsingEncoding:NSASCIIStringEncoding]; NSString* hex = [self hexStringWithData:data]; - [self testConvert:@"BINARY" input:hex length:1 output:data]; + [self testConvert:@"BINARY" style:1 input:hex output:data]; } -#pragma mark - Image +#pragma mark - VarBinary -- (void)testImageWithNull +- (void)testVarBinaryWithNull { id value = [NSNull null]; - [self testCast:@"IMAGE" input:nil output:value]; + [self testCast:@"VARBINARY" input:nil output:value]; } -- (void)testImageWithValue +- (void)testVarBinaryWithValue { - UIImage* image = [UIImage imageNamed:@"AppIcon40x40"]; - NSData* data = UIImagePNGRepresentation(image); + NSString* string = [self stringWithLength:30]; + NSData* data = [string dataUsingEncoding:NSASCIIStringEncoding]; NSString* hex = [self hexStringWithData:data]; - [self testConvert:@"IMAGE" input:hex length:1 output:data]; + [self testConvert:@"VARBINARY" style:1 input:hex output:data]; } #pragma mark - Private @@ -487,9 +487,9 @@ - (void)testCast:(NSString*)serverType input:(id)input output:(id)output [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } -- (void)testConvert:(NSString*)serverType input:(id)input length:(int)length output:(id)output +- (void)testConvert:(NSString*)serverType style:(int)style input:(id)input output:(id)output { - NSString* sql = [NSString stringWithFormat:@"SELECT CONVERT(%@, '%@', %d) AS Value", serverType, input, length]; + NSString* sql = [NSString stringWithFormat:@"SELECT CONVERT(%@, '%@', %d) AS Value", serverType, input, style]; XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { From e25a488c776d5b5f734679f8b6a36629e4898c57 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 2 Nov 2016 17:55:15 -0400 Subject: [PATCH 108/127] Don't query for max text size --- SQLClient/SQLClient/SQLClient/SQLClient.h | 7 +++++++ SQLClient/SQLClient/SQLClient/SQLClient.m | 6 ------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.h b/SQLClient/SQLClient/SQLClient/SQLClient.h index 70d17d8..b96041e 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.h +++ b/SQLClient/SQLClient/SQLClient/SQLClient.h @@ -32,6 +32,13 @@ extern NSString* _Nonnull const SQLClientSeverityKey; */ @property (nonatomic, copy, nonnull) NSString* charset; +/** + * The maximum text size to use for NVARCHAR(MAX) and TEXT data types. Default is 4096. + * Execute SELECT @@TEXTSIZE to determine the current setting on the server and use + * the result to update this property. + */ +@property (atomic, assign) int maxTextSize; + /** * Returns an initialized SQLClient instance as a singleton. * diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 3ad52a3..9b81af4 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -48,7 +48,6 @@ @interface SQLClient () @property (nonatomic, strong) NSOperationQueue* workerQueue; @property (nonatomic, weak) NSOperationQueue* callbackQueue; @property (atomic, assign, getter=isExecuting) BOOL executing; -@property (atomic, assign) int maxTextSize; @end @@ -168,11 +167,6 @@ - (void)connect:(nonnull NSString*)host //Success! [self connectionSuccess:completion]; [self cleanupAfterConnection]; - - //Query server for max text size - [self execute:@"SELECT @@TEXTSIZE AS MaxTextSize" completion:^(NSArray* results) { - self.maxTextSize = [results[0][0][@"MaxTextSize"] integerValue]; - }]; }]; } From 75bcff4087a601a9f07b9e8ccae4aa1ff010882f Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 2 Nov 2016 17:55:55 -0400 Subject: [PATCH 109/127] FreeTDS incorrectly returns an empty string for a single space --- README.md | 1 + SQLClient/SQLClientTests/SQLClientTests.m | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4b0f073..8b19a59 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ To configure the connection for your server: ## Known Issues PR's welcome! +* **strings**: FreeTDS incorrectly returns an empty string "" for a single space " " * **money**: FreeTDS will truncate the rightmost 2 digits. * OSX support: [FreeTDS-iOS](https://github.com/martinrybak/FreeTDS-iOS) needs to be compiled to support OSX and Podspec updated * No support for stored procedures with out parameters (yet) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index 46ad28b..f9bf399 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -365,7 +365,8 @@ - (void)testCharWithNull - (void)testCharWithMinimum { - //33 = minimum ASCII value (32 doesn't work) + //TODO: Fix (32 doesn't work) + //33 = minimum ASCII value id value = [NSString stringWithFormat:@"%c", 33]; [self testCast:@"CHAR(1)" input:value output:value]; } @@ -387,7 +388,8 @@ - (void)testVarCharMaxWithNull - (void)testVarCharMaxWithMinimum { - //33 = minimum ASCII value (32 doesn't work) + //TODO: Fix (32 doesn't work) + //33 = minimum ASCII value id value = [NSString stringWithFormat:@"%c", 33]; [self testCast:@"VARCHAR(MAX)" input:value output:value]; } @@ -409,7 +411,8 @@ - (void)testTextWithNull - (void)testTextWithMinimum { - //33 = minimum ASCII value (32 doesn't work) + //TODO: Fix (32 doesn't work) + //33 = minimum ASCII value id value = [NSString stringWithFormat:@"%c", 33]; [self testCast:@"TEXT" input:value output:value]; } From 6be9a908faa6bc5ea48030bc9414ced39f7486a6 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 2 Nov 2016 17:56:18 -0400 Subject: [PATCH 110/127] Image data type returned as NSData --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b19a59..ba7048d 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ SQLClient maps SQL Server data types into the following native Objective-C types * datetimeoffset → **NSDate** or **NSString**† * decimal(p,s) → NSNumber * float(n) → NSNumber -* image → **not supported** +* image → **NSData** * int → NSNumber * money → NSDecimalNumber **(last 2 digits are truncated)** * nchar → NSString From bcfcacd201e5e8561805983cd6d3abaaf35b6ac5 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 2 Nov 2016 20:37:30 -0400 Subject: [PATCH 111/127] Cleaned up the cleanup code --- SQLClient/SQLClient/SQLClient/SQLClient.m | 77 +++++++++++------------ 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 9b81af4..842fa74 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -57,6 +57,7 @@ @implementation SQLClient LOGINREC* _login; DBPROCESS* _connection; struct COLUMN* _columns; + int _numColumns; RETCODE _returnCode; } @@ -133,7 +134,6 @@ - (void)connect:(nonnull NSString*)host _login = dblogin(); if (_login == FAIL) { [self connectionFailure:completion]; - [self cleanupAfterConnection]; return; } @@ -150,7 +150,6 @@ - (void)connect:(nonnull NSString*)host _connection = dbopen(_login, [host UTF8String]); if (!_connection) { [self connectionFailure:completion]; - [self cleanupAfterConnection]; return; } @@ -159,14 +158,12 @@ - (void)connect:(nonnull NSString*)host _returnCode = dbuse(_connection, [database UTF8String]); if (_returnCode == FAIL) { [self connectionFailure:completion]; - [self cleanupAfterConnection]; return; } } //Success! [self connectionSuccess:completion]; - [self cleanupAfterConnection]; }]; } @@ -175,7 +172,7 @@ - (BOOL)isConnected return !dbdead(_connection); } -// TODO: get number of records changed during update or delete +// TODO: get number of records modified for update or delete commands // TODO: handle SQL stored procedure output parameters - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nullable results))completion { @@ -185,14 +182,12 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu if (!self.isConnected) { [self message:SQLClientNoConnectionError]; [self executionFailure:completion]; - [self cleanupAfterExecution:0]; return; } if (self.isExecuting) { [self message:SQLClientPendingExecutionError]; [self executionFailure:completion]; - [self cleanupAfterExecution:0]; return; } @@ -205,7 +200,6 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu _returnCode = dbcmd(_connection, [sql UTF8String]); if (_returnCode == FAIL) { [self executionFailure:completion]; - [self cleanupAfterExecution:0]; return; } @@ -213,7 +207,6 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu _returnCode = dbsqlexec(_connection); if (_returnCode == FAIL) { [self executionFailure:completion]; - [self cleanupAfterExecution:0]; return; } @@ -221,7 +214,7 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu NSMutableArray* output = [NSMutableArray array]; //Get number of columns - int numColumns = dbnumcols(_connection); + _numColumns = dbnumcols(_connection); //Loop through each table metadata //dbresults() returns SUCCEED, FAIL or, NO_MORE_RESULTS. @@ -229,26 +222,24 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu { if (_returnCode == FAIL) { [self executionFailure:completion]; - [self cleanupAfterExecution:0]; return; } - - struct COLUMN* column; - STATUS rowCode; //Create array to contain the rows for this table NSMutableArray* table = [NSMutableArray array]; //Allocate C-style array of COL structs - _columns = calloc(numColumns, sizeof(struct COLUMN)); + _columns = calloc(_numColumns, sizeof(struct COLUMN)); if (!_columns) { [self executionFailure:completion]; - [self cleanupAfterExecution:0]; return; } + struct COLUMN* column; + STATUS rowCode; + //Bind the column info - for (column = _columns; column - _columns < numColumns; column++) + for (column = _columns; column - _columns < _numColumns; column++) { //Get column number int c = column - _columns + 1; @@ -367,7 +358,6 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu column->data = calloc(1, column->size); if (!column->data) { [self executionFailure:completion]; - [self cleanupAfterExecution:numColumns]; return; } @@ -375,7 +365,6 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu _returnCode = dbbind(_connection, c, varType, column->size, column->data); if (_returnCode == FAIL) { [self executionFailure:completion]; - [self cleanupAfterExecution:numColumns]; return; } @@ -383,7 +372,6 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu _returnCode = dbnullbind(_connection, c, &column->status); if (_returnCode == FAIL) { [self executionFailure:completion]; - [self cleanupAfterExecution:numColumns]; return; } } @@ -398,10 +386,10 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu case REG_ROW: { //Create a new dictionary to contain the column names and vaues - NSMutableDictionary* row = [[NSMutableDictionary alloc] initWithCapacity:numColumns]; + NSMutableDictionary* row = [[NSMutableDictionary alloc] initWithCapacity:_numColumns]; //Loop through each column and create an entry where dictionary[columnName] = columnValue - for (column = _columns; column - _columns < numColumns; column++) + for (column = _columns; column - _columns < _numColumns; column++) { //Default to null id value = [NSNull null]; @@ -553,12 +541,10 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu //Buffer full case BUF_FULL: [self executionFailure:completion]; - [self cleanupAfterExecution:numColumns]; return; //Error case FAIL: [self executionFailure:completion]; - [self cleanupAfterExecution:numColumns]; return; default: [self message:SQLClientRowIgnoreMessage]; @@ -568,18 +554,17 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu //Add immutable copy of table to output [output addObject:[table copy]]; + [self cleanupAfterTable]; } //Success! Send an immutable copy of the results array [self executionSuccess:completion results:[output copy]]; - [self cleanupAfterExecution:numColumns]; }]; } - (void)disconnect { [self.workerQueue addOperationWithBlock:^{ - [self cleanupAfterExecution:0]; [self cleanupAfterConnection]; if (_connection) { dbclose(_connection); @@ -630,35 +615,44 @@ - (void)error:(NSString*)error code:(int)code severity:(int)severity #pragma mark - Cleanup -- (void)cleanupAfterConnection -{ - if (_login) { - dbloginfree(_login); - _login = NULL; - } - free(_password); - _password = NULL; -} - -- (void)cleanupAfterExecution:(int)numColumns +- (void)cleanupAfterTable { struct COLUMN* column; - for (column = _columns; column - _columns < numColumns; column++) { - free(column->data); - column->data = NULL; + for (column = _columns; column - _columns < _numColumns; column++) { + if (column) { + free(column->data); + column->data = NULL; + } } free(_columns); _columns = NULL; +} + +- (void)cleanupAfterExecution +{ + [self cleanupAfterTable]; if (_connection) { dbfreebuf(_connection); } } +- (void)cleanupAfterConnection +{ + [self cleanupAfterExecution]; + if (_login) { + dbloginfree(_login); + _login = NULL; + } + free(_password); + _password = NULL; +} + #pragma mark - Private //Invokes connection completion handler on callback queue with success = NO - (void)connectionFailure:(void (^)(BOOL success))completion { + [self cleanupAfterConnection]; [self.callbackQueue addOperationWithBlock:^{ if (completion) { completion(NO); @@ -669,6 +663,7 @@ - (void)connectionFailure:(void (^)(BOOL success))completion //Invokes connection completion handler on callback queue with success = [self connected] - (void)connectionSuccess:(void (^)(BOOL success))completion { + [self cleanupAfterConnection]; [self.callbackQueue addOperationWithBlock:^{ if (completion) { completion([self isConnected]); @@ -680,6 +675,7 @@ - (void)connectionSuccess:(void (^)(BOOL success))completion - (void)executionFailure:(void (^)(NSArray* results))completion { self.executing = NO; + [self cleanupAfterExecution]; [self.callbackQueue addOperationWithBlock:^{ if (completion) { completion(nil); @@ -691,6 +687,7 @@ - (void)executionFailure:(void (^)(NSArray* results))completion - (void)executionSuccess:(void (^)(NSArray* results))completion results:(NSArray*)results { self.executing = NO; + [self cleanupAfterExecution]; [self.callbackQueue addOperationWithBlock:^{ if (completion) { completion(results); From abd76b6cceecfac6ad729b8142d83d039ee7a653 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 2 Nov 2016 20:37:47 -0400 Subject: [PATCH 112/127] Added multiple table test --- SQLClient/SQLClientTests/SQLClientTests.m | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index f9bf399..a95df7c 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -471,6 +471,24 @@ - (void)testVarBinaryWithValue [self testConvert:@"VARBINARY" style:1 input:hex output:data]; } +#pragma mark - Multiple Tables + +- (void)testMultipleTables +{ + NSString* sql = @"SELECT * FROM (VALUES (1), (2), (3)) AS Table1(a); SELECT * FROM (VALUES (4), (5), (6)) AS Table2(b);"; + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:sql completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"a"], @(1)); + XCTAssertEqualObjects(results[0][1][@"a"], @(2)); + XCTAssertEqualObjects(results[0][2][@"a"], @(3)); + XCTAssertEqualObjects(results[1][0][@"b"], @(4)); + XCTAssertEqualObjects(results[1][1][@"b"], @(5)); + XCTAssertEqualObjects(results[1][2][@"b"], @(6)); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + #pragma mark - Private - (void)testCast:(NSString*)serverType input:(id)input output:(id)output From e7d5e3527bed39061c1a445e762023971f8029b2 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Wed, 2 Nov 2016 21:17:30 -0400 Subject: [PATCH 113/127] Updated decimal/numeric handling and added tests --- SQLClient/SQLClient/SQLClient/SQLClient.m | 6 ++++ SQLClient/SQLClientTests/SQLClientTests.m | 35 +++++++++++++++++++---- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 842fa74..525d715 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -304,6 +304,12 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu } case SYBDECIMAL: case SYBNUMERIC: + { + //Workaround for incorrect size + varType = CHARBIND; + column->size += 23; + break; + } case SYBCHAR: case SYBVARCHAR: case SYBNVARCHAR: diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index a95df7c..920c71c 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -157,16 +157,42 @@ - (void)testRealWithMaximum #pragma mark - Decimal -- (void)testDecimal +- (void)testDecimalWithNull { - + id value = [NSNull null]; + [self testCast:@"DECIMAL" input:nil output:value]; +} + +- (void)testDecimalWithMinimum +{ + id value = [[NSDecimalNumber decimalNumberWithMantissa:1 exponent:38 isNegative:YES] decimalNumberByAdding:[NSDecimalNumber one]]; + [self testCast:@"DECIMAL(38,0)" input:value output:value]; +} + +- (void)testDecimalWithMaximum +{ + id value = [[NSDecimalNumber decimalNumberWithMantissa:1 exponent:38 isNegative:NO] decimalNumberBySubtracting:[NSDecimalNumber one]]; + [self testCast:@"DECIMAL(38,0)" input:value output:value]; } #pragma mark - Numeric -- (void)testNumeric +- (void)testNumericWithNull { - + id value = [NSNull null]; + [self testCast:@"NUMERIC" input:nil output:value]; +} + +- (void)testNumericWithMinimum +{ + id value = [[NSDecimalNumber decimalNumberWithMantissa:1 exponent:38 isNegative:YES] decimalNumberByAdding:[NSDecimalNumber one]]; + [self testCast:@"NUMERIC(38,0)" input:value output:value]; +} + +- (void)testNumericWithMaximum +{ + id value = [[NSDecimalNumber decimalNumberWithMantissa:1 exponent:38 isNegative:NO] decimalNumberBySubtracting:[NSDecimalNumber one]]; + [self testCast:@"NUMERIC(38,0)" input:value output:value]; } #pragma mark - Small Money @@ -511,7 +537,6 @@ - (void)testCast:(NSString*)serverType input:(id)input output:(id)output - (void)testConvert:(NSString*)serverType style:(int)style input:(id)input output:(id)output { NSString* sql = [NSString stringWithFormat:@"SELECT CONVERT(%@, '%@', %d) AS Value", serverType, input, style]; - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { XCTAssertEqualObjects(results[0][0][@"Value"], output); From e935358ad67cb031a55c514a653055ac948362fb Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Thu, 3 Nov 2016 04:22:24 -0400 Subject: [PATCH 114/127] Updated example SQL --- SQLClient/SQLClient/SQLViewController.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SQLClient/SQLClient/SQLViewController.m b/SQLClient/SQLClient/SQLViewController.m index f2e4d07..c3b4dfd 100644 --- a/SQLClient/SQLClient/SQLViewController.m +++ b/SQLClient/SQLClient/SQLViewController.m @@ -71,7 +71,7 @@ - (void)connect { SQLClient* client = [SQLClient sharedInstance]; [self.spinner startAnimating]; - [client connect:@"server:port" username:@"user" password:@"pass" database:@"db" completion:^(BOOL success) { + [client connect:@"server\instance:port" username:@"user" password:@"pass" database:@"db" completion:^(BOOL success) { [self.spinner stopAnimating]; if (success) { [self execute]; @@ -83,7 +83,7 @@ - (void)execute { SQLClient* client = [SQLClient sharedInstance]; [self.spinner startAnimating]; - [client execute:@"SELECT * FROM Users" completion:^(NSArray* results) { + [client execute:@"SELECT * FROM Table" completion:^(NSArray* results) { [self.spinner stopAnimating]; [self process:results]; [client disconnect]; From 21a7b45b218479613eab86c162a2af2c60c17229 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Thu, 3 Nov 2016 04:23:16 -0400 Subject: [PATCH 115/127] Escaped backslash --- SQLClient/SQLClient/SQLViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLViewController.m b/SQLClient/SQLClient/SQLViewController.m index c3b4dfd..0d1e3e5 100644 --- a/SQLClient/SQLClient/SQLViewController.m +++ b/SQLClient/SQLClient/SQLViewController.m @@ -71,7 +71,7 @@ - (void)connect { SQLClient* client = [SQLClient sharedInstance]; [self.spinner startAnimating]; - [client connect:@"server\instance:port" username:@"user" password:@"pass" database:@"db" completion:^(BOOL success) { + [client connect:@"server\\instance:port" username:@"user" password:@"pass" database:@"db" completion:^(BOOL success) { [self.spinner stopAnimating]; if (success) { [self execute]; From ee3a450e0202fcd37eeae09616a29c9c707faf2e Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Thu, 3 Nov 2016 04:28:44 -0400 Subject: [PATCH 116/127] Fixed crash in table cleanup --- SQLClient/SQLClient/SQLClient/SQLClient.m | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 525d715..1e75941 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -623,15 +623,16 @@ - (void)error:(NSString*)error code:(int)code severity:(int)severity - (void)cleanupAfterTable { - struct COLUMN* column; - for (column = _columns; column - _columns < _numColumns; column++) { - if (column) { - free(column->data); - column->data = NULL; + if (_columns) { + for (struct COLUMN* column = _columns; column - _columns < _numColumns; column++) { + if (column) { + free(column->data); + column->data = NULL; + } } + free(_columns); + _columns = NULL; } - free(_columns); - _columns = NULL; } - (void)cleanupAfterExecution From 3407636fee4b890d58905d6eb286d00622cc1716 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Thu, 3 Nov 2016 04:46:35 -0400 Subject: [PATCH 117/127] Renamed bind type variable --- SQLClient/SQLClient/SQLClient/SQLClient.m | 36 +++++++++++------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 1e75941..de65edc 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -250,63 +250,63 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu column->size = dbcollen(_connection, c); //Set bind type based on column type - int varType = CHARBIND; //Default + int bindType = CHARBIND; //Default switch (column->type) { case SYBBIT: case SYBBITN: { - varType = BITBIND; + bindType = BITBIND; break; } case SYBINT1: { - varType = TINYBIND; + bindType = TINYBIND; break; } case SYBINT2: { - varType = SMALLBIND; + bindType = SMALLBIND; break; } case SYBINT4: case SYBINTN: { - varType = INTBIND; + bindType = INTBIND; break; } case SYBINT8: { - varType = BIGINTBIND; + bindType = BIGINTBIND; break; } case SYBFLT8: case SYBFLTN: { - varType = FLT8BIND; + bindType = FLT8BIND; break; } case SYBREAL: { - varType = REALBIND; + bindType = REALBIND; break; } case SYBMONEY4: { - varType = SMALLMONEYBIND; + bindType = SMALLMONEYBIND; break; } case SYBMONEY: case SYBMONEYN: { - varType = MONEYBIND; + bindType = MONEYBIND; break; } case SYBDECIMAL: case SYBNUMERIC: { //Workaround for incorrect size - varType = CHARBIND; + bindType = CHARBIND; column->size += 23; break; } @@ -316,7 +316,7 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu case SYBTEXT: case SYBNTEXT: { - varType = NTBSTRINGBIND; + bindType = NTBSTRINGBIND; column->size = MIN(column->size, self.maxTextSize); break; } @@ -326,13 +326,13 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu case SYBBIGDATETIME: case SYBBIGTIME: { - varType = DATETIMEBIND; + bindType = DATETIMEBIND; break; } case SYBDATE: case SYBMSDATE: { - varType = DATEBIND; + bindType = DATEBIND; break; } case SYBTIME: @@ -340,13 +340,13 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu { //Workaround for TIME data type. We have to increase the size and cast as string. column->size += 14; - varType = CHARBIND; + bindType = CHARBIND; break; } case SYBMSDATETIMEOFFSET: case SYBMSDATETIME2: { - varType = DATETIME2BIND; + bindType = DATETIME2BIND; break; } case SYBVOID: @@ -355,7 +355,7 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu case SYBVARBINARY: case SYBUNIQUEIDENTIFIER: { - varType = BINARYBIND; + bindType = BINARYBIND; break; } } @@ -368,7 +368,7 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu } //Bind column data - _returnCode = dbbind(_connection, c, varType, column->size, column->data); + _returnCode = dbbind(_connection, c, bindType, column->size, column->data); if (_returnCode == FAIL) { [self executionFailure:completion]; return; From 601385a870d295ec9c10f204478c551c5da2cc73 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Thu, 3 Nov 2016 05:15:53 -0400 Subject: [PATCH 118/127] Renamed tests for clarity --- SQLClient/SQLClientTests/SQLClientTests.m | 190 +++++++++++----------- 1 file changed, 98 insertions(+), 92 deletions(-) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index 920c71c..2ce0a44 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -20,19 +20,19 @@ @implementation SQLClientTests - (void)testBitWithNull { id value = [NSNull null]; - [self testCast:@"BIT" input:nil output:value]; + [self testValue:nil ofType:@"BIT" convertsTo:value]; } - (void)testBitWithTrue { id value = @(YES); - [self testCast:@"BIT" input:value output:value]; + [self testValue:value ofType:@"BIT" convertsTo:value]; } - (void)testBitWithFalse { id value = @(YES); - [self testCast:@"BIT" input:value output:value]; + [self testValue:value ofType:@"BIT" convertsTo:value]; } #pragma mark - Tiny Int @@ -40,19 +40,19 @@ - (void)testBitWithFalse - (void)testTinyIntWithNull { id value = [NSNull null]; - [self testCast:@"TINYINT" input:nil output:value]; + [self testValue:nil ofType:@"TINYINT" convertsTo:value]; } - (void)testTinyIntWithMinimum { id value = @(0); - [self testCast:@"TINYINT" input:value output:value]; + [self testValue:value ofType:@"TINYINT" convertsTo:value]; } - (void)testTinyIntWithMaximum { id value = @(UCHAR_MAX); - [self testCast:@"TINYINT" input:value output:value]; + [self testValue:value ofType:@"TINYINT" convertsTo:value]; } #pragma mark - Small Int @@ -60,19 +60,19 @@ - (void)testTinyIntWithMaximum - (void)testSmallIntWithNull { id value = [NSNull null]; - [self testCast:@"SMALLINT" input:nil output:value]; + [self testValue:nil ofType:@"SMALLINT" convertsTo:value]; } - (void)testSmallIntWithMinimum { id value = @(SHRT_MIN); - [self testCast:@"SMALLINT" input:value output:value]; + [self testValue:value ofType:@"SMALLINT" convertsTo:value]; } - (void)testSmallIntWithMaximum { id value = @(SHRT_MAX); - [self testCast:@"SMALLINT" input:value output:value]; + [self testValue:value ofType:@"SMALLINT" convertsTo:value]; } #pragma mark - Int @@ -80,19 +80,19 @@ - (void)testSmallIntWithMaximum - (void)testIntWithNull { id value = [NSNull null]; - [self testCast:@"INT" input:nil output:value]; + [self testValue:nil ofType:@"INT" convertsTo:value]; } - (void)testIntWithMinimum { id value = @(SHRT_MIN); - [self testCast:@"INT" input:value output:value]; + [self testValue:value ofType:@"INT" convertsTo:value]; } - (void)testIntWithMaximum { id value = @(SHRT_MAX); - [self testCast:@"INT" input:value output:value]; + [self testValue:value ofType:@"INT" convertsTo:value]; } #pragma mark - Big Int @@ -100,19 +100,19 @@ - (void)testIntWithMaximum - (void)testBigIntWithNull { id value = [NSNull null]; - [self testCast:@"BIGINT" input:nil output:value]; + [self testValue:nil ofType:@"BIGINT" convertsTo:value]; } - (void)testBigIntWithMinimum { id value = @(LLONG_MIN); - [self testCast:@"BIGINT" input:value output:value]; + [self testValue:value ofType:@"BIGINT" convertsTo:value]; } - (void)testBigIntWithMaximum { id value = @(LLONG_MAX); - [self testCast:@"BIGINT" input:value output:value]; + [self testValue:value ofType:@"BIGINT" convertsTo:value]; } #pragma mark - Float @@ -120,19 +120,19 @@ - (void)testBigIntWithMaximum - (void)testFloatWithNull { id value = [NSNull null]; - [self testCast:@"FLOAT" input:nil output:value]; + [self testValue:nil ofType:@"FLOAT" convertsTo:value]; } - (void)testFloatWithMinimum { id value = @(-1.79e+308); - [self testCast:@"FLOAT" input:value output:value]; + [self testValue:value ofType:@"FLOAT" convertsTo:value]; } - (void)testFloatWithMaximum { id value = @(1.79e+308); - [self testCast:@"FLOAT" input:value output:value]; + [self testValue:value ofType:@"FLOAT" convertsTo:value]; } #pragma mark - Real @@ -140,19 +140,19 @@ - (void)testFloatWithMaximum - (void)testRealWithNull { id value = [NSNull null]; - [self testCast:@"REAL" input:nil output:value]; + [self testValue:nil ofType:@"REAL" convertsTo:value]; } - (void)testRealWithMinimum { id value = [NSNumber numberWithFloat:-3.4e+38]; - [self testCast:@"REAL" input:value output:value]; + [self testValue:value ofType:@"REAL" convertsTo:value]; } - (void)testRealWithMaximum { id value = [NSNumber numberWithFloat:3.4e+38]; - [self testCast:@"REAL" input:value output:value]; + [self testValue:value ofType:@"REAL" convertsTo:value]; } #pragma mark - Decimal @@ -160,19 +160,19 @@ - (void)testRealWithMaximum - (void)testDecimalWithNull { id value = [NSNull null]; - [self testCast:@"DECIMAL" input:nil output:value]; + [self testValue:nil ofType:@"DECIMAL" convertsTo:value]; } - (void)testDecimalWithMinimum { id value = [[NSDecimalNumber decimalNumberWithMantissa:1 exponent:38 isNegative:YES] decimalNumberByAdding:[NSDecimalNumber one]]; - [self testCast:@"DECIMAL(38,0)" input:value output:value]; + [self testValue:value ofType:@"DECIMAL(38,0)" convertsTo:value]; } - (void)testDecimalWithMaximum { id value = [[NSDecimalNumber decimalNumberWithMantissa:1 exponent:38 isNegative:NO] decimalNumberBySubtracting:[NSDecimalNumber one]]; - [self testCast:@"DECIMAL(38,0)" input:value output:value]; + [self testValue:value ofType:@"DECIMAL(38,0)" convertsTo:value]; } #pragma mark - Numeric @@ -180,19 +180,19 @@ - (void)testDecimalWithMaximum - (void)testNumericWithNull { id value = [NSNull null]; - [self testCast:@"NUMERIC" input:nil output:value]; + [self testValue:nil ofType:@"NUMERIC" convertsTo:value]; } - (void)testNumericWithMinimum { id value = [[NSDecimalNumber decimalNumberWithMantissa:1 exponent:38 isNegative:YES] decimalNumberByAdding:[NSDecimalNumber one]]; - [self testCast:@"NUMERIC(38,0)" input:value output:value]; + [self testValue:value ofType:@"NUMERIC(38,0)" convertsTo:value]; } - (void)testNumericWithMaximum { id value = [[NSDecimalNumber decimalNumberWithMantissa:1 exponent:38 isNegative:NO] decimalNumberBySubtracting:[NSDecimalNumber one]]; - [self testCast:@"NUMERIC(38,0)" input:value output:value]; + [self testValue:value ofType:@"NUMERIC(38,0)" convertsTo:value]; } #pragma mark - Small Money @@ -200,19 +200,19 @@ - (void)testNumericWithMaximum - (void)testSmallMoneyWithNull { id value = [NSNull null]; - [self testCast:@"SMALLMONEY" input:nil output:value]; + [self testValue:nil ofType:@"SMALLMONEY" convertsTo:value]; } - (void)testSmallMoneyWithMinimum { id value = [NSDecimalNumber decimalNumberWithString:@"-214748.3648"]; - [self testCast:@"SMALLMONEY" input:value output:value]; + [self testValue:value ofType:@"SMALLMONEY" convertsTo:value]; } - (void)testSmallMoneyWithMaximum { id value = [NSDecimalNumber decimalNumberWithString:@"214748.3647"]; - [self testCast:@"SMALLMONEY" input:value output:value]; + [self testValue:value ofType:@"SMALLMONEY" convertsTo:value]; } #pragma mark - Money @@ -220,21 +220,21 @@ - (void)testSmallMoneyWithMaximum - (void)testMoneyWithNull { id value = [NSNull null]; - [self testCast:@"MONEY" input:nil output:value]; + [self testValue:nil ofType:@"MONEY" convertsTo:value]; } - (void)testMoneyWithMinimum { //TODO: fix last 2 digits, i.e. -922337203685477.5808 returns -922337203685477.58 id value = [NSDecimalNumber decimalNumberWithString:@"-922337203685477.58"]; - [self testCast:@"MONEY" input:value output:value]; + [self testValue:value ofType:@"MONEY" convertsTo:value]; } - (void)testMoneyWithMaximum { //TODO: fix last 2 digits, i.e. 922337203685477.5807 returns 922337203685477.58 id value = [NSDecimalNumber decimalNumberWithString:@"922337203685477.58"]; - [self testCast:@"MONEY" input:value output:value]; + [self testValue:value ofType:@"MONEY" convertsTo:value]; } #pragma mark - Small DateTime @@ -242,21 +242,21 @@ - (void)testMoneyWithMaximum - (void)testSmallDateTimeWithNull { id value = [NSNull null]; - [self testCast:@"SMALLDATETIME" input:nil output:value]; + [self testValue:nil ofType:@"SMALLDATETIME" convertsTo:value]; } - (void)testSmallDateTimeWithMinimum { - id value = @"01-01-1900 00:00:00"; + id input = @"01-01-1900 00:00:00"; id output = [self dateWithYear:1900 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; - [self testCast:@"SMALLDATETIME" input:value output:output]; + [self testValue:input ofType:@"SMALLDATETIME" convertsTo:output]; } - (void)testSmallDateTimeWithMaximum { - id value = @"06-06-2079 23:59:00"; + id input = @"06-06-2079 23:59:00"; id output = [self dateWithYear:2079 month:6 day:6 hour:23 minute:59 second:0 nanosecond:0 timezone:0]; - [self testCast:@"SMALLDATETIME" input:value output:output]; + [self testValue:input ofType:@"SMALLDATETIME" convertsTo:output]; } #pragma mark - DateTime @@ -264,21 +264,21 @@ - (void)testSmallDateTimeWithMaximum - (void)testDateTimeWithNull { id value = [NSNull null]; - [self testCast:@"DATETIME" input:nil output:value]; + [self testValue:nil ofType:@"DATETIME" convertsTo:value]; } - (void)testDateTimeWithMinimum { - id value = @"01-01-1753 00:00:00:000"; + id input = @"01-01-1753 00:00:00:000"; id output = [self dateWithYear:1753 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; - [self testCast:@"DATETIME" input:value output:output]; + [self testValue:input ofType:@"DATETIME" convertsTo:output]; } - (void)testDateTimeWithMaximum { - id value = @"12-31-9999 23:59:59:997"; + id input = @"12-31-9999 23:59:59:997"; id output = [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:997000000 timezone:0]; - [self testCast:@"DATETIME" input:value output:output]; + [self testValue:input ofType:@"DATETIME" convertsTo:output]; } #pragma mark - DateTime2 @@ -289,21 +289,21 @@ - (void)testDateTimeWithMaximum - (void)testDateTime2WithNull { id value = [NSNull null]; - [self testCast:@"DATETIME2" input:nil output:value]; + [self testValue:nil ofType:@"DATETIME2" convertsTo:value]; } - (void)testDateTime2WithMinimum { - id value = @"01-01-0001 00:00:00.0000000"; + id input = @"01-01-0001 00:00:00.0000000"; id output = [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; - [self testCast:@"DATETIME2" input:value output:output]; + [self testValue:input ofType:@"DATETIME2" convertsTo:output]; } - (void)testDateTime2WithMaximum { - id value = @"12-31-9999 23:59:59.9999999"; + id input = @"12-31-9999 23:59:59.9999999"; id output = [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:999999900 timezone:0]; - [self testCast:@"DATETIME2" input:value output:output]; + [self testValue:input ofType:@"DATETIME2" convertsTo:output]; } #pragma mark - DateTimeOffset @@ -314,21 +314,21 @@ - (void)testDateTime2WithMaximum - (void)testDateTimeOffsetWithNull { id value = [NSNull null]; - [self testCast:@"DATETIMEOFFSET" input:nil output:value]; + [self testValue:nil ofType:@"DATETIMEOFFSET" convertsTo:value]; } - (void)testDateTimeOffsetWithMinimum { - id value = @"01-01-0001 00:00:00.0000000 -14:00"; + id input = @"01-01-0001 00:00:00.0000000 -14:00"; id output = [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:-840]; - [self testCast:@"DATETIMEOFFSET" input:value output:output]; + [self testValue:input ofType:@"DATETIMEOFFSET" convertsTo:output]; } - (void)testDateTimeOffsetWithMaximum { - id value = @"12-31-9999 23:59:59.9999999 +14:00"; + id input = @"12-31-9999 23:59:59.9999999 +14:00"; id output = [self dateWithYear:9999 month:12 day:31 hour:23 minute:59 second:59 nanosecond:999999900 timezone:840]; - [self testCast:@"DATETIMEOFFSET" input:value output:output]; + [self testValue:input ofType:@"DATETIMEOFFSET" convertsTo:output]; } #pragma mark - Date @@ -339,21 +339,21 @@ - (void)testDateTimeOffsetWithMaximum - (void)testDateWithNull { id value = [NSNull null]; - [self testCast:@"DATE" input:nil output:value]; + [self testValue:nil ofType:@"DATE" convertsTo:value]; } - (void)testDateWithMinimum { - id value = @"01-01-0001"; + id input = @"01-01-0001"; id output = [self dateWithYear:1 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; - [self testCast:@"DATE" input:value output:output]; + [self testValue:input ofType:@"DATE" convertsTo:output]; } - (void)testDateWithMaximum { - id value = @"12-31-9999"; + id input = @"12-31-9999"; id output = [self dateWithYear:9999 month:12 day:31 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; - [self testCast:@"DATE" input:value output:output]; + [self testValue:input ofType:@"DATE" convertsTo:output]; } #pragma mark - Time @@ -364,21 +364,21 @@ - (void)testDateWithMaximum - (void)testTimeWithNull { id value = [NSNull null]; - [self testCast:@"TIME" input:nil output:value]; + [self testValue:nil ofType:@"TIME" convertsTo:value]; } - (void)testTimeWithMinimum { - id value = @"00:00:00.0000000"; + id input = @"00:00:00.0000000"; id output = [self dateWithYear:1900 month:1 day:1 hour:0 minute:0 second:0 nanosecond:0 timezone:0]; - [self testCast:@"TIME" input:value output:output]; + [self testValue:input ofType:@"TIME" convertsTo:output]; } - (void)testTimeWithMaximum { - id value = @"23:59:59.9999999"; + id input = @"23:59:59.9999999"; id output = [self dateWithYear:1900 month:1 day:1 hour:23 minute:59 second:59 nanosecond:999999900 timezone:0]; - [self testCast:@"TIME" input:value output:output]; + [self testValue:input ofType:@"TIME" convertsTo:output]; } #pragma mark - Char @@ -386,7 +386,7 @@ - (void)testTimeWithMaximum - (void)testCharWithNull { id value = [NSNull null]; - [self testCast:@"CHAR(1)" input:nil output:value]; + [self testValue:nil ofType:@"CHAR(1)" convertsTo:value]; } - (void)testCharWithMinimum @@ -394,14 +394,14 @@ - (void)testCharWithMinimum //TODO: Fix (32 doesn't work) //33 = minimum ASCII value id value = [NSString stringWithFormat:@"%c", 33]; - [self testCast:@"CHAR(1)" input:value output:value]; + [self testValue:value ofType:@"CHAR(1)" convertsTo:value]; } - (void)testCharWithMaximum { //127 = maximum printable ASCII value id value = [NSString stringWithFormat:@"%c", 127]; - [self testCast:@"CHAR(1)" input:value output:value]; + [self testValue:value ofType:@"CHAR(1)" convertsTo:value]; } #pragma mark - VarChar(Max) @@ -409,7 +409,7 @@ - (void)testCharWithMaximum - (void)testVarCharMaxWithNull { id value = [NSNull null]; - [self testCast:@"VARCHAR(MAX)" input:nil output:value]; + [self testValue:nil ofType:@"VARCHAR(MAX)" convertsTo:value]; } - (void)testVarCharMaxWithMinimum @@ -417,14 +417,14 @@ - (void)testVarCharMaxWithMinimum //TODO: Fix (32 doesn't work) //33 = minimum ASCII value id value = [NSString stringWithFormat:@"%c", 33]; - [self testCast:@"VARCHAR(MAX)" input:value output:value]; + [self testValue:value ofType:@"VARCHAR(MAX)" convertsTo:value]; } - (void)testVarCharMaxWithMaximum { - id value = [self stringWithLength:[SQLClient sharedInstance].maxTextSize + 1]; - id output = [value substringToIndex:[SQLClient sharedInstance].maxTextSize - 1]; - [self testCast:@"VARCHAR(MAX)" input:value output:output]; + id input = [self stringWithLength:[SQLClient sharedInstance].maxTextSize + 1]; + id output = [input substringToIndex:[SQLClient sharedInstance].maxTextSize - 1]; + [self testValue:input ofType:@"VARCHAR(MAX)" convertsTo:output]; } #pragma mark - Text @@ -432,7 +432,7 @@ - (void)testVarCharMaxWithMaximum - (void)testTextWithNull { id value = [NSNull null]; - [self testCast:@"TEXT" input:nil output:value]; + [self testValue:nil ofType:@"TEXT" convertsTo:value]; } - (void)testTextWithMinimum @@ -440,14 +440,14 @@ - (void)testTextWithMinimum //TODO: Fix (32 doesn't work) //33 = minimum ASCII value id value = [NSString stringWithFormat:@"%c", 33]; - [self testCast:@"TEXT" input:value output:value]; + [self testValue:value ofType:@"TEXT" convertsTo:value]; } - (void)testTextWithMaximum { - id value = [self stringWithLength:[SQLClient sharedInstance].maxTextSize + 1]; - id output = [value substringToIndex:[SQLClient sharedInstance].maxTextSize - 1]; - [self testCast:@"TEXT" input:value output:output]; + id input = [self stringWithLength:[SQLClient sharedInstance].maxTextSize + 1]; + id output = [input substringToIndex:[SQLClient sharedInstance].maxTextSize - 1]; + [self testValue:input ofType:@"TEXT" convertsTo:output]; } #pragma mark - UniqueIdentifier @@ -455,14 +455,14 @@ - (void)testTextWithMaximum - (void)testUniqueIdentifierWithNull { id value = [NSNull null]; - [self testCast:@"UNIQUEIDENTIFIER" input:nil output:value]; + [self testValue:nil ofType:@"UNIQUEIDENTIFIER" convertsTo:value]; } - (void)testUniqueIdentifierWithValue { id output = [NSUUID UUID]; - id value = [output UUIDString]; - [self testCast:@"UNIQUEIDENTIFIER" input:value output:output]; + id input = [output UUIDString]; + [self testValue:input ofType:@"UNIQUEIDENTIFIER" convertsTo:output]; } #pragma mark - Binary @@ -470,15 +470,15 @@ - (void)testUniqueIdentifierWithValue - (void)testBinaryWithNull { id value = [NSNull null]; - [self testCast:@"BINARY" input:nil output:value]; + [self testBinaryValue:nil ofType:@"BINARY" convertsTo:value withStyle:1]; } - (void)testBinaryWithValue { NSString* string = [self stringWithLength:30]; - NSData* data = [string dataUsingEncoding:NSASCIIStringEncoding]; - NSString* hex = [self hexStringWithData:data]; - [self testConvert:@"BINARY" style:1 input:hex output:data]; + NSData* output = [string dataUsingEncoding:NSASCIIStringEncoding]; + NSString* input = [self hexStringWithData:output]; + [self testBinaryValue:input ofType:@"BINARY" convertsTo:output withStyle:1]; } #pragma mark - VarBinary @@ -486,15 +486,15 @@ - (void)testBinaryWithValue - (void)testVarBinaryWithNull { id value = [NSNull null]; - [self testCast:@"VARBINARY" input:nil output:value]; + [self testBinaryValue:nil ofType:@"VARBINARY" convertsTo:value withStyle:1]; } - (void)testVarBinaryWithValue { NSString* string = [self stringWithLength:30]; - NSData* data = [string dataUsingEncoding:NSASCIIStringEncoding]; - NSString* hex = [self hexStringWithData:data]; - [self testConvert:@"VARBINARY" style:1 input:hex output:data]; + NSData* output = [string dataUsingEncoding:NSASCIIStringEncoding]; + NSString* input = [self hexStringWithData:output]; + [self testBinaryValue:input ofType:@"VARBINARY" convertsTo:output withStyle:1]; } #pragma mark - Multiple Tables @@ -517,13 +517,13 @@ - (void)testMultipleTables #pragma mark - Private -- (void)testCast:(NSString*)serverType input:(id)input output:(id)output +- (void)testValue:(id)input ofType:(NSString*)type convertsTo:(id)output { NSString* sql; if (input) { - sql = [NSString stringWithFormat:@"SELECT CAST('%@' AS %@) AS Value", input, serverType]; + sql = [NSString stringWithFormat:@"SELECT CAST('%@' AS %@) AS Value", input, type]; } else { - sql = [NSString stringWithFormat:@"SELECT CAST(NULL AS %@) AS Value", serverType]; + sql = [NSString stringWithFormat:@"SELECT CAST(NULL AS %@) AS Value", type]; } XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; @@ -534,9 +534,15 @@ - (void)testCast:(NSString*)serverType input:(id)input output:(id)output [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } -- (void)testConvert:(NSString*)serverType style:(int)style input:(id)input output:(id)output +- (void)testBinaryValue:(id)input ofType:(NSString*)type convertsTo:(id)output withStyle:(int)style { - NSString* sql = [NSString stringWithFormat:@"SELECT CONVERT(%@, '%@', %d) AS Value", serverType, input, style]; + NSString* sql; + if (input) { + sql = [NSString stringWithFormat:@"SELECT CONVERT(%@, '%@', %d) AS Value", type, input, style]; + } else { + sql = [NSString stringWithFormat:@"SELECT CONVERT(%@, NULL, %d) AS Value", type, style]; + } + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { XCTAssertEqualObjects(results[0][0][@"Value"], output); From 8ad3274490150bf99153ab95debe85635a123741 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 4 Nov 2016 06:27:31 -0400 Subject: [PATCH 119/127] Simplified multiple tables test --- SQLClient/SQLClientTests/SQLClientTests.m | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index 2ce0a44..e580727 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -501,15 +501,11 @@ - (void)testVarBinaryWithValue - (void)testMultipleTables { - NSString* sql = @"SELECT * FROM (VALUES (1), (2), (3)) AS Table1(a); SELECT * FROM (VALUES (4), (5), (6)) AS Table2(b);"; + NSString* sql = @"SELECT 'Bar' AS Foo; SELECT 'Foo' AS Bar;"; XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"a"], @(1)); - XCTAssertEqualObjects(results[0][1][@"a"], @(2)); - XCTAssertEqualObjects(results[0][2][@"a"], @(3)); - XCTAssertEqualObjects(results[1][0][@"b"], @(4)); - XCTAssertEqualObjects(results[1][1][@"b"], @(5)); - XCTAssertEqualObjects(results[1][2][@"b"], @(6)); + XCTAssertEqualObjects(results[0][0][@"Foo"], @"Bar"); + XCTAssertEqualObjects(results[1][0][@"Bar"], @"Foo"); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; From 8b30151452db02bdaec3ebbfdb37ea252542fa7a Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 4 Nov 2016 14:26:22 -0400 Subject: [PATCH 120/127] Added CRUD tests --- SQLClient/SQLClient/SQLClient/SQLClient.m | 6 +-- SQLClient/SQLClientTests/SQLClientTests.m | 60 ++++++++++++++++++++++- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index de65edc..01b9ca6 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -213,9 +213,6 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu //Create array to contain the tables NSMutableArray* output = [NSMutableArray array]; - //Get number of columns - _numColumns = dbnumcols(_connection); - //Loop through each table metadata //dbresults() returns SUCCEED, FAIL or, NO_MORE_RESULTS. while ((_returnCode = dbresults(_connection)) != NO_MORE_RESULTS) @@ -228,6 +225,9 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu //Create array to contain the rows for this table NSMutableArray* table = [NSMutableArray array]; + //Get number of columns + _numColumns = dbnumcols(_connection); + //Allocate C-style array of COL structs _columns = calloc(_numColumns, sizeof(struct COLUMN)); if (!_columns) { diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index e580727..9af07ac 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -15,6 +15,59 @@ @interface SQLClientTests : XCTestCase @implementation SQLClientTests +#pragma mark - CRUD + +- (void)testInsert +{ + NSMutableString* sql = [NSMutableString string]; + [sql appendString:@"CREATE TABLE #Temp(Id INT IDENTITY, Name CHAR(20));"]; + [sql appendString:@"INSERT INTO #Temp (Name) VALUES ('Foo');"]; + [sql appendString:@"SELECT @@IDENTITY AS Id;"]; + [sql appendString:@"DROP TABLE #Temp;"]; + + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:sql completion:^(NSArray* results) { + XCTAssertEqualObjects(results[1][0][@"Id"], @(1)); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:100 handler:nil]; +} + +- (void)testUpdate +{ + NSMutableString* sql = [NSMutableString string]; + [sql appendString:@"CREATE TABLE #Temp(Id INT IDENTITY, Name CHAR(20));"]; + [sql appendString:@"INSERT INTO #Temp (Name) VALUES ('Foo');"]; + [sql appendString:@"UPDATE #Temp SET Name = 'Bar';"]; + [sql appendString:@"SELECT Name FROM #Temp;"]; + [sql appendString:@"DROP TABLE #Temp;"]; + + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:sql completion:^(NSArray* results) { + XCTAssertEqualObjects(results[1][0][@"Name"], @"Bar"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:100 handler:nil]; +} + +- (void)testDelete +{ + NSMutableString* sql = [NSMutableString string]; + [sql appendString:@"CREATE TABLE #Temp(Id INT IDENTITY, Name CHAR(20));"]; + [sql appendString:@"INSERT INTO #Temp (Name) VALUES ('Foo');"]; + [sql appendString:@"DELETE FROM #Temp;"]; + [sql appendString:@"SELECT * FROM #Temp;"]; + [sql appendString:@"DROP TABLE #Temp;"]; + + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:sql completion:^(NSArray* results) { + XCTAssertNotNil(results); + XCTAssertEqual([results[1] count], 0); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:100 handler:nil]; +} + #pragma mark - Bit - (void)testBitWithNull @@ -499,9 +552,12 @@ - (void)testVarBinaryWithValue #pragma mark - Multiple Tables -- (void)testMultipleTables +- (void)testSelectMultipleTables { - NSString* sql = @"SELECT 'Bar' AS Foo; SELECT 'Foo' AS Bar;"; + NSMutableString* sql = [NSMutableString string]; + [sql appendString:@"SELECT 'Bar' AS Foo;"]; + [sql appendString:@"SELECT 'Foo' AS Bar;"]; + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { XCTAssertEqualObjects(results[0][0][@"Foo"], @"Bar"); From 51721d520f7f476f5f03e932f81209ad41276715 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 4 Nov 2016 14:47:15 -0400 Subject: [PATCH 121/127] Don't add empty array to output if table has no columns --- SQLClient/SQLClient/SQLClient/SQLClient.m | 11 +++-- SQLClient/SQLClientTests/SQLClientTests.m | 59 ++++++++++++++--------- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 01b9ca6..2654bef 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -221,13 +221,18 @@ - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nu [self executionFailure:completion]; return; } - - //Create array to contain the rows for this table - NSMutableArray* table = [NSMutableArray array]; //Get number of columns _numColumns = dbnumcols(_connection); + //No columns, skip to next table + if (!_numColumns) { + continue; + } + + //Create array to contain the rows for this table + NSMutableArray* table = [NSMutableArray array]; + //Allocate C-style array of COL structs _columns = calloc(_numColumns, sizeof(struct COLUMN)); if (!_columns) { diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index 9af07ac..ee2bdfb 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -17,6 +17,34 @@ @implementation SQLClientTests #pragma mark - CRUD +- (void)testSelectWithOneTable +{ + NSString* sql = @"SELECT 'Foo' AS Bar"; + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:sql completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Bar"], @"Foo"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testSelectWithMultipleTables +{ + NSMutableString* sql = [NSMutableString string]; + [sql appendString:@"SELECT 'Foo' AS Bar;"]; + [sql appendString:@"SELECT * FROM (VALUES (1), (2), (3)) AS Table1(a)"]; + + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:sql completion:^(NSArray* results) { + XCTAssertEqualObjects(results[0][0][@"Bar"], @"Foo"); + XCTAssertEqualObjects(results[1][0][@"a"], @(1)); + XCTAssertEqualObjects(results[1][1][@"a"], @(2)); + XCTAssertEqualObjects(results[1][2][@"a"], @(3)); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + - (void)testInsert { NSMutableString* sql = [NSMutableString string]; @@ -27,10 +55,10 @@ - (void)testInsert XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { - XCTAssertEqualObjects(results[1][0][@"Id"], @(1)); + XCTAssertEqualObjects(results[0][0][@"Id"], @(1)); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:100 handler:nil]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } - (void)testUpdate @@ -39,15 +67,15 @@ - (void)testUpdate [sql appendString:@"CREATE TABLE #Temp(Id INT IDENTITY, Name CHAR(20));"]; [sql appendString:@"INSERT INTO #Temp (Name) VALUES ('Foo');"]; [sql appendString:@"UPDATE #Temp SET Name = 'Bar';"]; - [sql appendString:@"SELECT Name FROM #Temp;"]; + [sql appendString:@"SELECT * FROM #Temp;"]; [sql appendString:@"DROP TABLE #Temp;"]; XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { - XCTAssertEqualObjects(results[1][0][@"Name"], @"Bar"); + XCTAssertEqualObjects(results[0][0][@"Name"], @"Bar"); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:100 handler:nil]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } - (void)testDelete @@ -62,10 +90,10 @@ - (void)testDelete XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { XCTAssertNotNil(results); - XCTAssertEqual([results[1] count], 0); + XCTAssertEqual([results[0] count], 0); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:100 handler:nil]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } #pragma mark - Bit @@ -550,23 +578,6 @@ - (void)testVarBinaryWithValue [self testBinaryValue:input ofType:@"VARBINARY" convertsTo:output withStyle:1]; } -#pragma mark - Multiple Tables - -- (void)testSelectMultipleTables -{ - NSMutableString* sql = [NSMutableString string]; - [sql appendString:@"SELECT 'Bar' AS Foo;"]; - [sql appendString:@"SELECT 'Foo' AS Bar;"]; - - XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; - [self execute:sql completion:^(NSArray* results) { - XCTAssertEqualObjects(results[0][0][@"Foo"], @"Bar"); - XCTAssertEqualObjects(results[1][0][@"Bar"], @"Foo"); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; -} - #pragma mark - Private - (void)testValue:(id)input ofType:(NSString*)type convertsTo:(id)output From 3687248b80a8d3ba148a710c785b99da967d9ebc Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 4 Nov 2016 15:03:09 -0400 Subject: [PATCH 122/127] Updated CRUD tests --- SQLClient/SQLClientTests/SQLClientTests.m | 65 +++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index ee2bdfb..d3b1630 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -22,6 +22,9 @@ - (void)testSelectWithOneTable NSString* sql = @"SELECT 'Foo' AS Bar"; XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { + XCTAssertNotNil(results); + XCTAssertEqual(results.count, 1); + XCTAssertEqual([results[0] count], 1); XCTAssertEqualObjects(results[0][0][@"Bar"], @"Foo"); [expectation fulfill]; }]; @@ -36,6 +39,10 @@ - (void)testSelectWithMultipleTables XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { + XCTAssertNotNil(results); + XCTAssertEqual(results.count, 2); + XCTAssertEqual([results[0] count], 1); + XCTAssertEqual([results[1] count], 3); XCTAssertEqualObjects(results[0][0][@"Bar"], @"Foo"); XCTAssertEqualObjects(results[1][0][@"a"], @(1)); XCTAssertEqualObjects(results[1][1][@"a"], @(2)); @@ -46,6 +53,22 @@ - (void)testSelectWithMultipleTables } - (void)testInsert +{ + NSMutableString* sql = [NSMutableString string]; + [sql appendString:@"CREATE TABLE #Temp(Id INT IDENTITY, Name CHAR(20));"]; + [sql appendString:@"INSERT INTO #Temp (Name) VALUES ('Foo');"]; + [sql appendString:@"DROP TABLE #Temp;"]; + + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:sql completion:^(NSArray* results) { + XCTAssertNotNil(results); + XCTAssertEqual(results.count, 0); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testInsertWithSelect { NSMutableString* sql = [NSMutableString string]; [sql appendString:@"CREATE TABLE #Temp(Id INT IDENTITY, Name CHAR(20));"]; @@ -55,6 +78,9 @@ - (void)testInsert XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { + XCTAssertNotNil(results); + XCTAssertEqual(results.count, 1); + XCTAssertEqual([results[0] count], 1); XCTAssertEqualObjects(results[0][0][@"Id"], @(1)); [expectation fulfill]; }]; @@ -62,6 +88,23 @@ - (void)testInsert } - (void)testUpdate +{ + NSMutableString* sql = [NSMutableString string]; + [sql appendString:@"CREATE TABLE #Temp(Id INT IDENTITY, Name CHAR(20));"]; + [sql appendString:@"INSERT INTO #Temp (Name) VALUES ('Foo');"]; + [sql appendString:@"UPDATE #Temp SET Name = 'Bar';"]; + [sql appendString:@"DROP TABLE #Temp;"]; + + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:sql completion:^(NSArray* results) { + XCTAssertNotNil(results); + XCTAssertEqual(results.count, 0); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testUpdateWithSelect { NSMutableString* sql = [NSMutableString string]; [sql appendString:@"CREATE TABLE #Temp(Id INT IDENTITY, Name CHAR(20));"]; @@ -72,6 +115,10 @@ - (void)testUpdate XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { + XCTAssertNotNil(results); + XCTAssertEqual(results.count, 1); + XCTAssertEqual([results[0] count], 1); + XCTAssertEqualObjects(results[0][0][@"Id"], @(1)); XCTAssertEqualObjects(results[0][0][@"Name"], @"Bar"); [expectation fulfill]; }]; @@ -79,6 +126,23 @@ - (void)testUpdate } - (void)testDelete +{ + NSMutableString* sql = [NSMutableString string]; + [sql appendString:@"CREATE TABLE #Temp(Id INT IDENTITY, Name CHAR(20));"]; + [sql appendString:@"INSERT INTO #Temp (Name) VALUES ('Foo');"]; + [sql appendString:@"DELETE FROM #Temp;"]; + [sql appendString:@"DROP TABLE #Temp;"]; + + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:sql completion:^(NSArray* results) { + XCTAssertNotNil(results); + XCTAssertEqual(results.count, 0); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + +- (void)testDeleteWithSelect { NSMutableString* sql = [NSMutableString string]; [sql appendString:@"CREATE TABLE #Temp(Id INT IDENTITY, Name CHAR(20));"]; @@ -90,6 +154,7 @@ - (void)testDelete XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; [self execute:sql completion:^(NSArray* results) { XCTAssertNotNil(results); + XCTAssertEqual(results.count, 1); XCTAssertEqual([results[0] count], 0); [expectation fulfill]; }]; From f153c48234b135b13ef67d4868f1a3e091d7359e Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 4 Nov 2016 15:58:23 -0400 Subject: [PATCH 123/127] Updated comment --- SQLClient/SQLClient/SQLClient/SQLClient.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.m b/SQLClient/SQLClient/SQLClient/SQLClient.m index 2654bef..71e0938 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.m +++ b/SQLClient/SQLClient/SQLClient/SQLClient.m @@ -172,7 +172,7 @@ - (BOOL)isConnected return !dbdead(_connection); } -// TODO: get number of records modified for update or delete commands +// TODO: get number of records modified for insert/update/delete commands // TODO: handle SQL stored procedure output parameters - (void)execute:(nonnull NSString*)sql completion:(nullable void(^)(NSArray* _Nullable results))completion { From 9ba52d0f6fdf08ed21e01f4a58f4c46cc55e9c7a Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Fri, 4 Nov 2016 20:05:16 -0400 Subject: [PATCH 124/127] Added test for nil results --- SQLClient/SQLClientTests/SQLClientTests.m | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/SQLClient/SQLClientTests/SQLClientTests.m b/SQLClient/SQLClientTests/SQLClientTests.m index d3b1630..11884cd 100644 --- a/SQLClient/SQLClientTests/SQLClientTests.m +++ b/SQLClient/SQLClientTests/SQLClientTests.m @@ -17,6 +17,16 @@ @implementation SQLClientTests #pragma mark - CRUD +- (void)testSelectWithError +{ + XCTestExpectation* expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)]; + [self execute:@"SELECT" completion:^(NSArray* results) { + XCTAssertNil(results); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; +} + - (void)testSelectWithOneTable { NSString* sql = @"SELECT 'Foo' AS Bar"; @@ -31,7 +41,7 @@ - (void)testSelectWithOneTable [self waitForExpectationsWithTimeout:[SQLClient sharedInstance].timeout handler:nil]; } -- (void)testSelectWithMultipleTables +- (void)testSelectWithTwoTables { NSMutableString* sql = [NSMutableString string]; [sql appendString:@"SELECT 'Foo' AS Bar;"]; From 927bdf29fd83e6583262cc706dbaa73f965064e0 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 8 Nov 2016 04:57:30 -0500 Subject: [PATCH 125/127] Updated docs for maxTextSize --- README.md | 2 +- SQLClient/SQLClient/SQLClient/SQLClient.h | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ba7048d..a592823 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ SQLClient maps SQL Server data types into the following native Objective-C types * varchar(n) → NSString * xml → NSString -*The maximum length of a string in a query is configured on the server via the `SET TEXTSIZE` command. SQLClient uses this length or defaults to `4096` if not available. +*The maximum length of a string in a query is configured on the server via the `SET TEXTSIZE` command. To find out your current setting, execute `SELECT @@TEXTSIZE`. SQLClient uses **4096** by default. To override this setting, update the `maxTextSize` property. †The following data types are only converted to **NSDate** on TDS version **7.3** and higher. By default FreeTDS uses version **7.1** of the TDS protocol, which converts them to **NSString**. To use a higher version of the TDS protocol, add an environment variable to Xcode named `TDSVER`. Possible values are `4.2`, `5.0`, `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `auto`. diff --git a/SQLClient/SQLClient/SQLClient/SQLClient.h b/SQLClient/SQLClient/SQLClient/SQLClient.h index b96041e..4f3ff54 100644 --- a/SQLClient/SQLClient/SQLClient/SQLClient.h +++ b/SQLClient/SQLClient/SQLClient/SQLClient.h @@ -33,9 +33,9 @@ extern NSString* _Nonnull const SQLClientSeverityKey; @property (nonatomic, copy, nonnull) NSString* charset; /** - * The maximum text size to use for NVARCHAR(MAX) and TEXT data types. Default is 4096. - * Execute SELECT @@TEXTSIZE to determine the current setting on the server and use - * the result to update this property. + * The maximum length of a string in a query is configured on the server via the SET TEXTSIZE command. + * To find out your current setting, execute SELECT @@TEXTSIZE. SQLClient uses 4096 by default. + * To override this setting, update this property. */ @property (atomic, assign) int maxTextSize; @@ -71,11 +71,6 @@ extern NSString* _Nonnull const SQLClientSeverityKey; */ - (BOOL)isExecuting; -/** - * Returns the maximum text size configured on the server (default 4096). - */ -- (int)maxTextSize; - /** * Executes a SQL statement. Results of queries will be passed to the completion handler. Inserts, updates, and deletes do not return results. * From 78bdde75e999dbf8581e25fdfb184719acbe21ff Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 8 Nov 2016 05:03:29 -0500 Subject: [PATCH 126/127] Escaped asterisk --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a592823..25ff88e 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ SQLClient maps SQL Server data types into the following native Objective-C types * varchar(n) → NSString * xml → NSString -*The maximum length of a string in a query is configured on the server via the `SET TEXTSIZE` command. To find out your current setting, execute `SELECT @@TEXTSIZE`. SQLClient uses **4096** by default. To override this setting, update the `maxTextSize` property. +\*The maximum length of a string in a query is configured on the server via the `SET TEXTSIZE` command. To find out your current setting, execute `SELECT @@TEXTSIZE`. SQLClient uses **4096** by default. To override this setting, update the `maxTextSize` property. †The following data types are only converted to **NSDate** on TDS version **7.3** and higher. By default FreeTDS uses version **7.1** of the TDS protocol, which converts them to **NSString**. To use a higher version of the TDS protocol, add an environment variable to Xcode named `TDSVER`. Possible values are `4.2`, `5.0`, `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `auto`. From d1f3ed4acd3ee70f0858639b8efeaa7e2f8669f1 Mon Sep 17 00:00:00 2001 From: Martin Rybak Date: Tue, 8 Nov 2016 17:05:43 -0500 Subject: [PATCH 127/127] Updated podspec with OS X binary --- SQLClient.podspec | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/SQLClient.podspec b/SQLClient.podspec index 595a35d..b2c692d 100644 --- a/SQLClient.podspec +++ b/SQLClient.podspec @@ -7,10 +7,17 @@ Pod::Spec.new do |s| s.authors = { 'Martin Rybak' => 'martin.rybak@gmail.com' } s.source = { :git => 'https://github.com/martinrybak/SQLClient.git', :tag => s.version.to_s } s.source_files = 'SQLClient/SQLClient/SQLClient/*.{h,m}' - s.vendored_libraries = 'SQLClient/SQLClient/SQLClient/libsybdb.a' s.libraries = 'iconv' s.requires_arc = true + s.ios.deployment_target = '7.0' + s.ios.vendored_libraries = 'SQLClient/SQLClient/SQLClient/libsybdb-ios.a' + + s.osx.deployment_target = '10.7' + s.osx.vendored_libraries = 'SQLClient/SQLClient/SQLClient/libsybdb-osx.a' + s.tvos.deployment_target = '9.0' + s.tvos.vendored_libraries = 'SQLClient/SQLClient/SQLClient/libsybdb-ios.a' + end