Last active
February 27, 2017 05:59
-
-
Save ramsince88/8535996 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SQLClient.h | |
// SQLClient | |
// | |
// Created by Martin Rybak on 10/4/13. | |
// Copyright (c) 2013 Martin Rybak. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
@protocol SQLClientDelegate <NSObject> | |
/** | |
* 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 | |
/** | |
* Native SQL Server client for iOS. An Objective-C wrapper around the open-source FreeTDS library. | |
*/ | |
@interface SQLClient : NSObject | |
/** | |
* Connection timeout, in seconds. Default is 5. Override before calling connect: | |
*/ | |
@property (nonatomic, assign) int timeout; | |
/** | |
* The database server, i.e. server, server:port, or server\instance (be sure to escape the backslash) | |
*/ | |
@property (nonatomic, copy, readonly) NSString* host; | |
/** | |
* The database username | |
*/ | |
@property (nonatomic, copy, readonly) NSString* username; | |
/** | |
* The database name to use | |
*/ | |
@property (nonatomic, copy, readonly) NSString* database; | |
/** | |
* The delegate to receive error: and message: callbacks | |
*/ | |
@property (nonatomic, weak) NSObject<SQLClientDelegate>* 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 | |
* | |
* @return Shared SQLClient object | |
*/ | |
+ (id)sharedInstance; | |
/** | |
* 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 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; | |
/** | |
* Indicates whether the database is currently connected | |
*/ | |
- (BOOL)connected; | |
/** | |
* 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. | |
*/ | |
- (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion; | |
/** | |
* Disconnects from database server | |
*/ | |
- (void)disconnect; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SQLClient.m | |
// SQLClient | |
// | |
// Created by Martin Rybak on 10/4/13. | |
// Copyright (c) 2013 Martin Rybak. All rights reserved. | |
// | |
#import "SQLClient.h" | |
#import "sybfront.h" | |
#import "sybdb.h" | |
#import "syberror.h" | |
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 SQLClientRowIgnoreMessage = @"Ignoring unknown row type"; | |
struct COL | |
{ | |
char* name; | |
char* buffer; | |
int type; | |
int size; | |
int status; | |
}; | |
@interface SQLClient () | |
@property (nonatomic, copy, readwrite) NSString* host; | |
@property (nonatomic, copy, readwrite) NSString* username; | |
@property (nonatomic, copy, readwrite) NSString* database; | |
@property (nonatomic, copy, readwrite) NSString* password; | |
@end | |
@implementation SQLClient | |
{ | |
LOGINREC* login; | |
DBPROCESS* connection; | |
} | |
#pragma mark - NSObject | |
//Initializes the FreeTDS library and sets callback handlers | |
- (id)init | |
{ | |
if (self = [super init]) | |
{ | |
//Initialize the FreeTDS library | |
if (dbinit() == FAIL) | |
return nil; | |
//Initialize SQLClient | |
self.timeout = SQLClientDefaultTimeout; | |
self.charset = SQLClientDefaultCharset; | |
self.callbackQueue = [NSOperationQueue currentQueue]; | |
self.workerQueue = [[NSOperationQueue alloc] init]; | |
self.workerQueue.name = SQLClientWorkerQueueName; | |
//Set FreeTDS callback handlers | |
dberrhandle(err_handler); | |
dbmsghandle(msg_handler); | |
} | |
return self; | |
} | |
//Exits the FreeTDS library | |
- (void)dealloc | |
{ | |
dbexit(); | |
} | |
#pragma mark - Public | |
+ (id)sharedInstance | |
{ | |
static SQLClient* sharedInstance = nil; | |
static dispatch_once_t onceToken; | |
dispatch_once(&onceToken, ^{ | |
sharedInstance = [[self alloc] init]; | |
}); | |
return sharedInstance; | |
} | |
- (void)connect:(NSString*)host | |
username:(NSString*)username | |
password:(NSString*)password | |
database:(NSString*)database | |
completion:(void (^)(BOOL success))completion | |
{ | |
//Save inputs | |
self.host = host; | |
self.username = username; | |
self.password = password; | |
self.database = database; | |
//Connect to database on worker queue | |
[self.workerQueue addOperationWithBlock:^{ | |
//Set login timeout | |
dbsetlogintime(self.timeout); | |
//Initialize login struct | |
if ((login = dblogin()) == FAIL) | |
return [self connectionFailure:completion]; | |
//Populate login struct | |
DBSETLUSER(login, [self.username UTF8String]); | |
DBSETLPWD(login, [self.password UTF8String]); | |
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]; | |
NSLog(@"%s", [self.database UTF8String]); | |
//Switch to database | |
if (dbuse(connection, [self.database UTF8String]) == FAIL) | |
return [self connectionFailure:completion]; | |
//Success! | |
[self connectionSuccess:completion]; | |
}]; | |
} | |
- (BOOL)connected | |
{ | |
return !dbdead(connection); | |
} | |
// TODO: how to get number of records changed during update or delete | |
// TODO: how to handle SQL stored procedure output parameters | |
- (void)execute:(NSString*)sql completion:(void (^)(NSArray* results))completion | |
{ | |
//Execute query on worker queue | |
[self.workerQueue addOperationWithBlock:^{ | |
//Prepare SQL statement | |
dbcmd(connection, [sql UTF8String]); | |
//Execute SQL statement | |
if (dbsqlexec(connection) == FAIL) | |
return [self executionFailure:completion]; | |
//Create array to contain the tables | |
NSMutableArray* output = [[NSMutableArray alloc] init]; | |
struct COL* columns; | |
struct COL* pcol; | |
int erc; | |
//Loop through each table | |
while ((erc = dbresults(connection)) != NO_MORE_RESULTS) | |
{ | |
int ncols; | |
int row_code; | |
//Create array to contain the rows for this table | |
NSMutableArray* table = [[NSMutableArray alloc] init]; | |
//Get number of columns | |
ncols = dbnumcols(connection); | |
//Allocate C-style array of COL structs | |
if ((columns = calloc(ncols, sizeof(struct COL))) == NULL) | |
return [self executionFailure:completion]; | |
//Bind the column info | |
for (pcol = columns; pcol - columns < ncols; pcol++) | |
{ | |
//Get column number | |
int c = pcol - columns + 1; | |
//Get column metadata | |
pcol->name = dbcolname(connection, c); | |
pcol->type = dbcoltype(connection, c); | |
pcol->size = dbcollen(connection, c); | |
//If the column is [VAR]CHAR, 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). | |
if (pcol->type != SYBCHAR) | |
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]; | |
//Bind column name | |
erc = dbbind(connection, c, NTBSTRINGBIND, pcol->size + 1, (BYTE*)pcol->buffer); | |
if (erc == FAIL) | |
return [self executionFailure:completion]; | |
//Bind column status | |
erc = dbnullbind(connection, c, &pcol->status); | |
if (erc == FAIL) | |
return [self executionFailure:completion]; | |
//printf("%s is type %d with value %s\n", pcol->name, pcol->type, pcol->buffer); | |
} | |
//printf("\n"); | |
//Loop through each row | |
while ((row_code = dbnextrow(connection)) != NO_MORE_ROWS) | |
{ | |
//Check row type | |
switch (row_code) | |
{ | |
//Regular row | |
case REG_ROW: | |
{ | |
//Create a new dictionary to contain the column names and vaues | |
NSMutableDictionary* row = [[NSMutableDictionary alloc] initWithCapacity:ncols]; | |
//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 = [NSString stringWithUTF8String:pcol->buffer] ?: [NSNull null]; | |
row[column] = value; | |
//printf("%@=%@\n", column, value); | |
} | |
//Add an immutable copy to the table | |
[table addObject:[row copy]]; | |
//printf("\n"); | |
break; | |
} | |
//Buffer full | |
case BUF_FULL: | |
return [self executionFailure:completion]; | |
//Error | |
case FAIL: | |
return [self executionFailure:completion]; | |
default: | |
[self message:SQLClientRowIgnoreMessage]; | |
} | |
} | |
//Clean up | |
for (pcol = columns; pcol - columns < ncols; pcol++) | |
free(pcol->buffer); | |
free(columns); | |
//Add immutable copy of table to output | |
[output addObject:[table copy]]; | |
} | |
//Success! Send an immutable copy of the results array | |
[self executionSuccess:completion results:[output copy]]; | |
}]; | |
} | |
- (void)disconnect | |
{ | |
dbclose(connection); | |
} | |
#pragma mark - Private | |
//Invokes connection completion handler on callback queue with success = NO | |
- (void)connectionFailure:(void (^)(BOOL success))completion | |
{ | |
[self.callbackQueue addOperationWithBlock:^{ | |
if (completion) | |
completion(NO); | |
}]; | |
//Cleanup | |
dbloginfree(login); | |
} | |
//Invokes connection completion handler on callback queue with success = [self connected] | |
- (void)connectionSuccess:(void (^)(BOOL success))completion | |
{ | |
[self.callbackQueue addOperationWithBlock:^{ | |
if (completion) | |
completion([self connected]); | |
}]; | |
//Cleanup | |
dbloginfree(login); | |
} | |
//Invokes execution completion handler on callback queue with results = nil | |
- (void)executionFailure:(void (^)(NSArray* results))completion | |
{ | |
[self.callbackQueue addOperationWithBlock:^{ | |
if (completion) | |
completion(nil); | |
}]; | |
//Clean up | |
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) | |
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; | |
} | |
//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 | |
{ | |
if (!self.delegate || ![self.delegate conformsToProtocol:@protocol(SQLClientDelegate)]) | |
[NSException raise:SQLClientDelegateError format:nil]; | |
//Invoke delegate on callback queue | |
[self.callbackQueue addOperationWithBlock:^{ | |
[self.delegate error:error code:code severity:severity]; | |
}]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
您好!我现在用这个库进行数据库访问!在进行上架的时候遇到了IPV6被拒问题,就是在审核的时候测试账号一直无法与数据库进行连接!按照苹果提供的在本地搭建IPV6测试环境进行测试的时候可以正常访问,但是每次上架审核的时候就会因为IPV6问题被拒!可以帮助解决一下吗,十分感谢!