From e124a1d0fb576c311a6ac601b1c08e6ce51bcd30 Mon Sep 17 00:00:00 2001 From: stuconnolly Date: Mon, 3 Sep 2012 10:22:17 +0000 Subject: Initial commit of PostgresKit, our new Postgres framework as a start towards adding PostgreSQL support to Sequel Pro. Note, that the framerwork is by no means feature complete and in it's current state has quite a few limitations: - No support for Postgres' asynchronous query API - Only supports the very basic data types (char/text and numerics) - No support (outide of libpq) for re-establishing dropped connections Current feature support includes: - Basic connection handling - Query execution - Prepared statement execution - Encoding support similar to SPMySQL's --- Frameworks/PostgresKit/Source/FLXConstants.h | 41 ++ Frameworks/PostgresKit/Source/FLXConstants.m | 31 ++ .../PostgresKit/Source/FLXPostgresConnection.h | 97 +++++ .../PostgresKit/Source/FLXPostgresConnection.m | 460 +++++++++++++++++++++ .../Source/FLXPostgresConnectionDelegate.h | 58 +++ .../Source/FLXPostgresConnectionEncoding.h | 39 ++ .../Source/FLXPostgresConnectionEncoding.m | 197 +++++++++ .../Source/FLXPostgresConnectionParameters.h | 56 +++ .../Source/FLXPostgresConnectionParameters.m | 220 ++++++++++ .../Source/FLXPostgresConnectionPrivateAPI.h | 38 ++ .../Source/FLXPostgresConnectionPrivateAPI.m | 29 ++ .../Source/FLXPostgresConnectionQueryExecution.h | 38 ++ .../Source/FLXPostgresConnectionQueryExecution.m | 322 +++++++++++++++ .../Source/FLXPostgresConnectionQueryPreparation.h | 32 ++ .../Source/FLXPostgresConnectionQueryPreparation.m | 131 ++++++ .../Source/FLXPostgresConnectionTypeHandling.h | 33 ++ .../Source/FLXPostgresConnectionTypeHandling.m | 105 +++++ .../Source/FLXPostgresConnectionUtils.h | 33 ++ .../Source/FLXPostgresConnectionUtils.m | 133 ++++++ Frameworks/PostgresKit/Source/FLXPostgresError.h | 74 ++++ Frameworks/PostgresKit/Source/FLXPostgresError.m | 136 ++++++ .../PostgresKit/Source/FLXPostgresException.h | 28 ++ .../PostgresKit/Source/FLXPostgresException.m | 64 +++ Frameworks/PostgresKit/Source/FLXPostgresResult.h | 71 ++++ Frameworks/PostgresKit/Source/FLXPostgresResult.m | 264 ++++++++++++ .../PostgresKit/Source/FLXPostgresStatement.h | 44 ++ .../PostgresKit/Source/FLXPostgresStatement.m | 81 ++++ .../Source/FLXPostgresTypeDateTimeHandler.h | 35 ++ .../Source/FLXPostgresTypeDateTimeHandler.m | 86 ++++ .../PostgresKit/Source/FLXPostgresTypeHandler.h | 38 ++ .../PostgresKit/Source/FLXPostgresTypeHandler.m | 46 +++ .../Source/FLXPostgresTypeHandlerProtocol.h | 84 ++++ .../Source/FLXPostgresTypeNumberHandler.h | 28 ++ .../Source/FLXPostgresTypeNumberHandler.m | 325 +++++++++++++++ .../Source/FLXPostgresTypeStringHandler.h | 28 ++ .../Source/FLXPostgresTypeStringHandler.m | 77 ++++ Frameworks/PostgresKit/Source/FLXPostgresTypes.h | 80 ++++ .../PostgresKit/Source/PostgresKit-Prefix.pch | 31 ++ Frameworks/PostgresKit/Source/PostgresKit.h | 27 ++ 39 files changed, 3740 insertions(+) create mode 100644 Frameworks/PostgresKit/Source/FLXConstants.h create mode 100644 Frameworks/PostgresKit/Source/FLXConstants.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnection.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnection.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionDelegate.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionEncoding.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionEncoding.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionParameters.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionParameters.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionPrivateAPI.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionPrivateAPI.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryExecution.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryExecution.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryPreparation.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryPreparation.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionTypeHandling.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionTypeHandling.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionUtils.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresConnectionUtils.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresError.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresError.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresException.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresException.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresResult.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresResult.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresStatement.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresStatement.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresTypeDateTimeHandler.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresTypeDateTimeHandler.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresTypeHandler.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresTypeHandler.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresTypeHandlerProtocol.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresTypeNumberHandler.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresTypeNumberHandler.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresTypeStringHandler.h create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresTypeStringHandler.m create mode 100644 Frameworks/PostgresKit/Source/FLXPostgresTypes.h create mode 100644 Frameworks/PostgresKit/Source/PostgresKit-Prefix.pch create mode 100644 Frameworks/PostgresKit/Source/PostgresKit.h (limited to 'Frameworks/PostgresKit/Source') diff --git a/Frameworks/PostgresKit/Source/FLXConstants.h b/Frameworks/PostgresKit/Source/FLXConstants.h new file mode 100644 index 00000000..95ab17f6 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXConstants.h @@ -0,0 +1,41 @@ +// +// $Id$ +// +// FLXConstants.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// Result set row types +typedef enum +{ + FLXPostgresResultRowAsArray = 1, + FLXPostgresResultRowAsDictionary = 2 +} +FLXPostgresResultRowType; + +// Defaults +extern NSString *FLXPostgresConnectionDefaultEncoding; +extern NSString *FLXPostgresConnectionErrorDomain; +extern NSStringEncoding FLXPostgresConnectionDefaultStringEncoding; + +// Server parameters +extern NSString *FLXPostgresParameterServerEncoding; +extern NSString *FLXPostgresParameterClientEncoding; +extern NSString *FLXPostgresParameterSuperUser; +extern NSString *FLXPostgresParameterTimeZone; +extern NSString *FLXPostgresParameterIntegerDateTimes; diff --git a/Frameworks/PostgresKit/Source/FLXConstants.m b/Frameworks/PostgresKit/Source/FLXConstants.m new file mode 100644 index 00000000..3dc68ff9 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXConstants.m @@ -0,0 +1,31 @@ +// +// $Id$ +// +// FLXConstants.m +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +NSString *FLXPostgresConnectionDefaultEncoding = @"UNICODE"; +NSString *FLXPostgresConnectionErrorDomain = @"FLXPostgresConnectionError"; +NSStringEncoding FLXPostgresConnectionDefaultStringEncoding = NSUTF8StringEncoding; + +NSString *FLXPostgresParameterServerEncoding = @"server_encoding"; +NSString *FLXPostgresParameterClientEncoding = @"client_encoding"; +NSString *FLXPostgresParameterSuperUser = @"is_superuser"; +NSString *FLXPostgresParameterTimeZone = @"TimeZone"; +NSString *FLXPostgresParameterIntegerDateTimes = @"integer_datetimes"; diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnection.h b/Frameworks/PostgresKit/Source/FLXPostgresConnection.h new file mode 100644 index 00000000..20260818 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnection.h @@ -0,0 +1,97 @@ +// +// $Id$ +// +// FLXPostgresConnection.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresTypeHandlerProtocol.h" +#import "FLXPostgresConnectionDelegate.h" + +@class FLXPostgresResult; +@class FLXPostgresStatement; +@class FLXPostgresConnectionParameters; + +@interface FLXPostgresConnection : NSObject +{ + PGconn *_connection; + + NSString *_host; + NSString *_user; + NSString *_database; + NSString *_password; + NSString *_socketPath; + NSString *_lastErrorMessage; + NSString *_encoding; + + const char **_connectionParamNames; + const char **_connectionParamValues; + + NSStringEncoding _stringEncoding; + + NSUInteger _port; + NSUInteger _timeout; + NSUInteger _keepAliveInterval; + + BOOL _useSocket; + BOOL _useKeepAlive; + BOOL _lastQueryWasCancelled; + BOOL _delegateSupportsWillExecute; + + NSMutableDictionary *_typeMap; + + FLXPostgresConnectionParameters *_parameters; + + NSObject *_delegate; +} + +@property (readwrite, assign) NSObject *delegate; + +@property (readwrite, retain) NSString *host; +@property (readwrite, retain) NSString *user; +@property (readwrite, retain) NSString *database; +@property (readwrite, retain) NSString *password; +@property (readwrite, retain) NSString *socketPath; + +@property (readonly) NSString *encoding; +@property (readonly) NSString *lastErrorMessage; +@property (readonly) NSStringEncoding stringEncoding; +@property (readonly) FLXPostgresConnectionParameters *parameters; + +@property (readwrite, assign) BOOL useSocket; +@property (readwrite, assign) BOOL useKeepAlive; +@property (readwrite, assign) BOOL lastQueryWasCancelled; + +@property (readwrite, assign) NSUInteger timeout; +@property (readwrite, assign) NSUInteger port; +@property (readwrite, assign) NSUInteger keepAliveInterval; + +- (id)initWithDelegate:(NSObject *)delegate; + +- (BOOL)connect; +- (void)disconnect; +- (BOOL)isConnected; +- (BOOL)reset; + +- (NSUInteger)clientVersion; +- (NSUInteger)serverVersion; +- (NSUInteger)serverProcessId; + +- (BOOL)cancelCurrentQuery:(NSError **)error; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnection.m b/Frameworks/PostgresKit/Source/FLXPostgresConnection.m new file mode 100644 index 00000000..3bd24bb2 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnection.m @@ -0,0 +1,460 @@ +// +// $Id$ +// +// FLXPostgresConnection.m +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresConnection.h" +#import "FLXPostgresConnectionParameters.h" +#import "FLXPostgresConnectionTypeHandling.h" +#import "FLXPostgresConnectionPrivateAPI.h" +#import "FLXPostgresTypeNumberHandler.h" +#import "FLXPostgresTypeStringHandler.h" +#import "FLXPostgresException.h" +#import "FLXPostgresStatement.h" +#import "FLXPostgresResult.h" + +#import "pthread.h" + +// Connection default constants +static NSUInteger FLXPostgresConnectionDefaultTimeout = 30; +static NSUInteger FLXPostgresConnectionDefaultServerPort = 5432; +static NSUInteger FLXPostgresConnectionDefaultKeepAlive = 60; + +// libpq connection parameters +static const char *FLXPostgresApplicationName = "PostgresKit"; +static const char *FLXPostgresApplicationParam = "application_name"; +static const char *FLXPostgresUserParam = "user"; +static const char *FLXPostgresHostParam = "host"; +static const char *FLXPostgresPasswordParam = "password"; +static const char *FLXPostgresPortParam = "port"; +static const char *FLXPostgresDatabaseParam = "dbname"; +static const char *FLXPostgresConnectionTimeoutParam = "conect_timeout"; +static const char *FLXPostgresClientEncodingParam = "client_encoding"; +static const char *FLXPostgresKeepAliveParam = "keepalives"; +static const char *FLXPostgresKeepAliveIntervalParam = "keepalives_interval"; + +@interface FLXPostgresConnection () + +- (void)_pollConnection; +- (void)_loadDatabaseParameters; +- (void)_createConnectionParameters; + +// libpq callback +static void _FLXPostgresConnectionNoticeProcessor(void *arg, const char *message); + +@end + +@implementation FLXPostgresConnection + +@synthesize port = _port; +@synthesize host = _host; +@synthesize user = _user; +@synthesize database = _database; +@synthesize password = _password; +@synthesize useSocket = _useSocket; +@synthesize socketPath = _socketPath; +@synthesize delegate = _delegate; +@synthesize timeout = _timeout; +@synthesize useKeepAlive = _useKeepAlive; +@synthesize keepAliveInterval = _keepAliveInterval; +@synthesize lastQueryWasCancelled = _lastQueryWasCancelled; +@synthesize lastErrorMessage = _lastErrorMessage; +@synthesize encoding = _encoding; +@synthesize stringEncoding = _stringEncoding; +@synthesize parameters = _parameters; + +#pragma mark - +#pragma mark Initialisation + +- (id)init +{ + return [self initWithDelegate:nil]; +} + +/** + * Initialise a new connection with the supplied delegate. + * + * @param delegate The delegate this connection should use. + * + * @return The new connection instance. + */ +- (id)initWithDelegate:(NSObject *)delegate +{ + if ((self = [super init])) { + + _delegate = delegate; + + _port = FLXPostgresConnectionDefaultServerPort; + _timeout = FLXPostgresConnectionDefaultTimeout; + + _useKeepAlive = YES; + _keepAliveInterval = FLXPostgresConnectionDefaultKeepAlive; + + _connection = nil; + _lastErrorMessage = nil; + _lastQueryWasCancelled = NO; + + _stringEncoding = FLXPostgresConnectionDefaultStringEncoding; + _encoding = [NSString stringWithString:FLXPostgresConnectionDefaultEncoding]; + + _delegateSupportsWillExecute = [_delegate respondsToSelector:@selector(connection:willExecute:values:)]; + + _typeMap = [[NSMutableDictionary alloc] init]; + + [self registerTypeHandlers]; + } + + return self; +} + +#pragma mark - +#pragma mark Accessors + +- (PGconn *)postgresConnection +{ + return _connection; +} + +#pragma mark - +#pragma mark Connection Handling + +/** + * Does this connection have an underlying connection established with the server. + * + * @return A BOOL indicating the result of the query. + */ +- (BOOL)isConnected +{ + if (!_connection) return NO; + + return PQstatus(_connection) == CONNECTION_OK; +} + +/** + * Attempts to disconnect the underlying connection with the server. + */ +- (void)disconnect +{ + if (!_connection) return; + + [self cancelCurrentQuery:nil]; + + PQfinish(_connection); + + _connection = nil; + + if (_delegate && [_delegate respondsToSelector:@selector(connectionDisconnected:)]) { + [_delegate connectionDisconnected:self]; + } +} + +/** + * Initiates the underlying connection to the server asynchronously. + * + * Note, that if no user, host or database is set when connect is called, then libpq's defaults are used. + * For no host, this means a socket connection to /tmp is attempted. + * + * @return A BOOL indicating the success of requesting the connection. Note, that this does not indicate + * that a successful connection has been made, only that it has successfullly been requested. + */ +- (BOOL)connect +{ + if ([self isConnected]) { + [FLXPostgresException raise:FLXPostgresConnectionErrorDomain reason:@"Attempt to initiate a connection that is already active"]; + + return NO; + } + + [self _createConnectionParameters]; + + // Perform the connection + _connection = PQconnectStartParams(_connectionParamNames, _connectionParamValues, 0); + + if (!_connection || PQstatus(_connection) == CONNECTION_BAD) { + + // TODO: implement reconnection attempt + return NO; + } + + [self performSelectorInBackground:@selector(_pollConnection) withObject:nil]; + + return YES; +} + +/** + * Attempts the reset the underlying connection. + * + * @note A return value of NO means that the connection is not currently + * connected to be reset and YES means the reset request was successful, + * not that the connection re-establishment has succeeded. Use -isConnected + * to check this. + * + * @return A BOOL indicating the success of the call. + */ +- (BOOL)reset +{ + if (![self isConnected]) return NO; + + PQreset(_connection); + + return YES; +} + +/** + * Returns the PostgreSQL client library (libpq) version being used. + * + * @return The library version (e.g. version 9.1 is 90100). + */ +- (NSUInteger)clientVersion +{ + return PQlibVersion(); +} + +/** + * Returns the version of the server we're connected to. + * + * @return The server version (e.g. version 9.1 is 90100). Zero is returned if there's no connection. + */ +- (NSUInteger)serverVersion +{ + if (![self isConnected]) return 0; + + return PQserverVersion(_connection); +} + +/** + * Returns the ID of the process handling this connection on the remote host. + * + * @return The process ID or -1 if no connection is available. + */ +- (NSUInteger)serverProcessId +{ + if (![self isConnected]) return -1; + + return PQbackendPID(_connection); +} + +/** + * Attempts to cancel the query currently executing on this connection. + * + * @param error Populated if query was unabled to be cancelled. + * + * @return A BOOL indicating the success of the request + */ +- (BOOL)cancelCurrentQuery:(NSError **)error +{ + if (![self isConnected]) return NO; + + PGcancel *cancel = PQgetCancel(_connection); + + if (!cancel) return NO; + + char errorBuf[256]; + + int result = PQcancel(cancel, errorBuf, 256); + + PQfreeCancel(cancel); + + if (!result) { + if (error != NULL) { + *error = [NSError errorWithDomain:FLXPostgresConnectionErrorDomain + code:0 + userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithUTF8String:errorBuf] forKey:NSLocalizedDescriptionKey]]; + } + + return NO; + } + + _lastQueryWasCancelled = YES; + + return YES; +} + +#pragma mark - +#pragma mark Private API + +/** + * Polls the connection that was previously requested via -connect and waits for meaninful status. + * + * @note This method should be called on a background thread as it will block waiting for the connection. + */ +- (void)_pollConnection +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + BOOL failed = NO; + BOOL connected = NO; + + while (!connected && !failed) + { + switch (PQconnectPoll(_connection)) + { + case PGRES_POLLING_READING: + case PGRES_POLLING_WRITING: + case PGRES_POLLING_ACTIVE: // Obsolete so we don't really care about it + break; + case PGRES_POLLING_OK: + connected = YES; + break; + case PGRES_POLLING_FAILED: + failed = YES; + break; + } + } + + if (connected) { + PQsetNoticeProcessor(_connection, _FLXPostgresConnectionNoticeProcessor, self); + + [self _loadDatabaseParameters]; + + if (_delegate && [_delegate respondsToSelector:@selector(connectionEstablished:)]) { + [_delegate performSelectorOnMainThread:@selector(connectionEstablished:) withObject:self waitUntilDone:NO]; + } + } + + [pool release]; +} + +/** + * Loads the database parameters. + */ +- (void)_loadDatabaseParameters +{ + if (_parameters) [_parameters release]; + + _parameters = [[FLXPostgresConnectionParameters alloc] initWithConnection:self]; + + BOOL success = [_parameters loadParameters]; + + if (!success) { + NSLog(@"PostgresKit: Warning: Failed to load database parameters."); + } +} + +/** + * libpq notice processor function. Simply passes the message onto the connection delegate. + * + * @param arg The calling connection. + * @param message The message that was sent. + */ +static void _FLXPostgresConnectionNoticeProcessor(void *arg, const char *message) +{ + FLXPostgresConnection *connection = (FLXPostgresConnection *)arg; + + if ([connection isKindOfClass:[FLXPostgresConnection class]]) { + + if ([connection delegate] && [[connection delegate] respondsToSelector:@selector(connection:notice:)]) { + [[connection delegate] connection:connection notice:[NSString stringWithUTF8String:message]]; + } + } +} + +/** + * Creates the parameter arrays required to establish a connection. + */ +- (void)_createConnectionParameters +{ + BOOL hasUser = NO; + BOOL hasHost = NO; + BOOL hasPassword = NO; + BOOL hasDatabase = NO; + + if (_connectionParamNames) free(_connectionParamNames); + if (_connectionParamValues) free(_connectionParamValues); + + int paramCount = 6; + + if (_user && [_user length]) paramCount++, hasUser = YES; + if (_host && [_host length]) paramCount++, hasHost = YES; + if (_password && [_password length]) paramCount++, hasPassword = YES; + if (_database && [_database length]) paramCount++, hasDatabase = YES; + + _connectionParamNames = malloc(paramCount * sizeof(*_connectionParamNames)); + _connectionParamValues = malloc(paramCount * sizeof(*_connectionParamValues)); + + _connectionParamNames[0] = FLXPostgresApplicationParam; + _connectionParamValues[0] = FLXPostgresApplicationName; + + _connectionParamNames[1] = FLXPostgresPortParam; + _connectionParamValues[1] = [[[NSNumber numberWithUnsignedInteger:_port] stringValue] UTF8String]; + + _connectionParamNames[2] = FLXPostgresConnectionTimeoutParam; + _connectionParamValues[2] = [[[NSNumber numberWithUnsignedInteger:_timeout] stringValue] UTF8String]; + + _connectionParamNames[3] = FLXPostgresClientEncodingParam; + _connectionParamValues[3] = [_encoding UTF8String]; + + _connectionParamNames[4] = FLXPostgresKeepAliveParam; + _connectionParamValues[4] = _useKeepAlive ? "1" : "0"; + + _connectionParamNames[5] = FLXPostgresKeepAliveIntervalParam; + _connectionParamValues[5] = [[[NSNumber numberWithUnsignedInteger:_keepAliveInterval] stringValue] UTF8String]; + + NSUInteger i = 6; + + if (hasUser) { + _connectionParamNames[i] = FLXPostgresUserParam; + _connectionParamValues[i] = [_user UTF8String]; + + i++; + } + + if (hasHost) { + _connectionParamNames[i] = FLXPostgresHostParam; + _connectionParamValues[i] = [_host UTF8String]; + + i++; + } + + if (hasPassword) { + _connectionParamNames[i] = FLXPostgresPasswordParam; + _connectionParamValues[i] = [_password UTF8String]; + + i++; + } + + if (hasDatabase) { + _connectionParamNames[i] = FLXPostgresDatabaseParam; + _connectionParamValues[i] = [_database UTF8String]; + } +} + +#pragma mark - + +- (void)dealloc +{ + [_typeMap release]; + + [self disconnect]; + + [self setHost:nil]; + [self setUser:nil]; + [self setDatabase:nil]; + + if (_connectionParamNames) free(_connectionParamNames); + if (_connectionParamValues) free(_connectionParamValues); + + if (_parameters) [_parameters release], _parameters = nil; + if (_lastErrorMessage) [_lastErrorMessage release], _lastErrorMessage = nil; + + [super dealloc]; +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionDelegate.h b/Frameworks/PostgresKit/Source/FLXPostgresConnectionDelegate.h new file mode 100644 index 00000000..9626d8e1 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionDelegate.h @@ -0,0 +1,58 @@ +// +// $Id$ +// +// FLXPostgresConnectionDelegate.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +@class FLXPostgresConnection; + +@protocol FLXPostgresConnectionDelegate + +/** + * Called whenever a connection is successfully established. + * + * @param connection The connection instance. + */ +- (void)connectionEstablished:(FLXPostgresConnection *)connection; + +/** + * Called whenever a connection is disconnected. + * + * @param connection The connection instance. + */ +- (void)connectionDisconnected:(FLXPostgresConnection *)connection; + +/** + * Called whenever a message is received from the PostgreSQL server. + * + * @param connection The connection instance. + * @param notice The notice message received. + */ +- (void)connection:(FLXPostgresConnection *)connection notice:(NSString *)notice; + +/** + * Called just before a query is about to be executed. + * + * @param connection The connection executing the query. + * @param query The query about the be executed. + * @param values The values of the query. + */ +- (void)connection:(FLXPostgresConnection *)connection willExecute:(NSObject *)query values:(NSArray *)values; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionEncoding.h b/Frameworks/PostgresKit/Source/FLXPostgresConnectionEncoding.h new file mode 100644 index 00000000..c6686465 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionEncoding.h @@ -0,0 +1,39 @@ +// $Id$ +// +// FLXPostgresConnectionEncoding.h +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on August 4, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +#import "FLXPostgresConnection.h" + +@interface FLXPostgresConnection (FLXPostgresConnectionEncoding) + +- (BOOL)setEncoding:(NSString *)encoding; + ++ (NSStringEncoding)stringEncodingForPostgreSQLCharset:(const char *)charset; ++ (NSString *)postgreSQLCharsetForStringEncoding:(NSStringEncoding)stringEncoding; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionEncoding.m b/Frameworks/PostgresKit/Source/FLXPostgresConnectionEncoding.m new file mode 100644 index 00000000..ddecee64 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionEncoding.m @@ -0,0 +1,197 @@ +// +// $Id$ +// +// FLXPostgresConnectionEncoding.m +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on August 4, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +#import "FLXPostgresConnectionEncoding.h" +#import "FLXPostgresConnectionPrivateAPI.h" + +@implementation FLXPostgresConnection (FLXPostgresConnectionEncoding) + +/** + * Set the current connection's encoding. + * + * @param encoding The name of the encoding to use. + * + * @return A BOOL indicating the success of the operation. NO means there was either no connection or the + * encoding name wasn't recognised by the server. + */ +- (BOOL)setEncoding:(NSString *)encoding +{ + if (![self isConnected]) return NO; + + if ([_encoding isEqualToString:encoding]) return YES; + + if (PQsetClientEncoding(_connection, [encoding UTF8String]) != 0) return NO; + + [_encoding release], _encoding = [[NSString alloc] initWithString:encoding]; + + _stringEncoding = [FLXPostgresConnection stringEncodingForPostgreSQLCharset:[encoding UTF8String]]; + + return YES; +} + +/** + * Translates the supplied encoding name to it's corresponding string encoding identifier. + * + * @param charset The character set as a char array. + * + * @return The string encoding identifier. + */ ++ (NSStringEncoding)stringEncodingForPostgreSQLCharset:(const char *)charset +{ + if (!strcmp(charset, "UNICODE") || !strcmp(charset, "MULE_INTERNAL")) { + return NSUTF8StringEncoding; + } + else if (!strcmp(charset, "LATIN1")) { + return NSISOLatin1StringEncoding; + } + else if (!strcmp(charset, "LATIN2")) { + return NSISOLatin2StringEncoding; + } + else if (!strcmp(charset, "LATIN3")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatin3); + } + else if (!strcmp(charset, "LATIN4")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatin4); + } + else if (!strcmp(charset, "LATIN5")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatin5); + } + else if (!strcmp(charset, "LATIN6")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatin6); + } + else if (!strcmp(charset, "LATIN7")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatin7); + } + else if (!strcmp(charset, "LATIN8")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatin8); + } + else if (!strcmp(charset, "LATIN9")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatin9); + } + else if (!strcmp(charset, "LATIN10")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatin10); + } + else if (!strcmp(charset, "SQL_ASCII")) { + return NSASCIIStringEncoding; + } + else if (!strcmp(charset, "EUC_JP")) { + return NSJapaneseEUCStringEncoding; + } + else if (!strcmp(charset, "EUC_CN")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingEUC_CN); + } + else if (!strcmp(charset, "EUC_KR")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingEUC_KR); + } + else if (!strcmp(charset, "JOHAB")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingWindowsKoreanJohab); + } + else if (!strcmp(charset, "EUC_TW")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingEUC_TW); + } + else if (!strcmp(charset, "ISO_8859_5")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatinCyrillic); + } + else if (!strcmp(charset, "ISO_8859_6")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatinArabic); + } + else if (!strcmp(charset, "ISO_8859_7")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatinGreek); + } + else if (!strcmp(charset, "ISO_8859_8")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingISOLatinHebrew); + } + else if (!strcmp(charset, "KOI8")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingKOI8_R); + } + else if (!strcmp(charset, "ALT")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingDOSRussian); + } + else if (!strcmp(charset, "WIN")) { + return NSWindowsCP1251StringEncoding; + } + else if (!strcmp(charset, "WIN1256")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingWindowsArabic); + } + else if (!strcmp(charset, "TCVN")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingWindowsVietnamese); + } + else if (!strcmp(charset, "WIN874")) { + return CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingDOSThai); + } + + NSLog(@"PostgresKit: Warning: Unable to process unknown PostgreSQL encoding '%s'; falling back to UTF8.", charset); + + return FLXPostgresConnectionDefaultStringEncoding; +} + +/** + * Translates the supplied encoding identifier to it's corresponding encoding name. + * + * @param stringEncoding The string encoding to translate + * + * @return The encoding name as a string or nil if there's no mapping. + */ ++ (NSString *)postgreSQLCharsetForStringEncoding:(NSStringEncoding)stringEncoding +{ + switch (stringEncoding) + { + case NSASCIIStringEncoding: + return @"SQL_ASCII"; + + case NSJapaneseEUCStringEncoding: + return @"EUC_JP"; + + case NSUTF8StringEncoding: + case NSNonLossyASCIIStringEncoding: + return @"UNICODE"; + + case NSISOLatin1StringEncoding: + case NSWindowsCP1252StringEncoding: + return @"LATIN1"; + + case NSISOLatin2StringEncoding: + case NSWindowsCP1250StringEncoding: + return @"LATIN2"; + + case NSWindowsCP1251StringEncoding: + return @"WIN"; + + case NSWindowsCP1253StringEncoding: + return @"ISO_8859_7"; + + case NSWindowsCP1254StringEncoding: + return @"LATIN5"; + } + + return nil; +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionParameters.h b/Frameworks/PostgresKit/Source/FLXPostgresConnectionParameters.h new file mode 100644 index 00000000..84864a1a --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionParameters.h @@ -0,0 +1,56 @@ +// +// $Id$ +// +// FLXPostgresConnectionParameters.h +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on August 29, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +#import + +@class FLXPostgresConnection; + +@interface FLXPostgresConnectionParameters : NSObject +{ + FLXPostgresConnection *_connection; + + NSMutableArray *_parameterNames; + NSMutableDictionary *_parameters; + + pthread_mutex_t _readLock; +} + +/** + * @property connection The database connection to use. + */ +@property (readwrite, assign) FLXPostgresConnection *connection; + +- (id)initWithConnection:(FLXPostgresConnection *)connection; + +- (BOOL)loadParameters; + +- (id)valueForParameter:(NSString *)parameter; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionParameters.m b/Frameworks/PostgresKit/Source/FLXPostgresConnectionParameters.m new file mode 100644 index 00000000..f1161f89 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionParameters.m @@ -0,0 +1,220 @@ +// +// $Id$ +// +// FLXPostgresConnectionParameters.m +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on August 29, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +#import "FLXPostgresConnectionParameters.h" +#import "FLXPostgresConnectionPrivateAPI.h" +#import "FLXPostgresConnection.h" + +@interface FLXPostgresConnectionParameters () + +- (void)_loadParameters:(id)object; +- (BOOL)_isBooleanParameterValue:(NSString *)value; +- (BOOL)_booleanForParameterValue:(NSString *)value; + +@end + +@implementation FLXPostgresConnectionParameters + +@synthesize connection = _connection; + +#pragma mark - + +- (id)init +{ + return [self initWithConnection:nil]; +} + +/** + * Initialise a parameters instance with the supplied connection. + * + * @param connection The connection to use. + * + * @return The initialised instance. + */ +- (id)initWithConnection:(FLXPostgresConnection *)connection +{ + if ((self = [super init])) { + _connection = connection; + + pthread_mutex_init(&_readLock, NULL); + } + + return self; +} + +#pragma mark - +#pragma mark Public API + +/** + * Loads the database parameters. + * + * @return A BOOL indicating the success of the load. + */ +- (BOOL)loadParameters +{ + pthread_mutex_lock(&_readLock); + + if (!_connection || ![_connection isConnected]) { + pthread_mutex_unlock(&_readLock); + + return NO; + } + + if (!_parameterNames) { + _parameterNames = [[NSMutableArray alloc] init]; + + [_parameterNames addObject:FLXPostgresParameterServerEncoding]; + [_parameterNames addObject:FLXPostgresParameterClientEncoding]; + [_parameterNames addObject:FLXPostgresParameterSuperUser]; + [_parameterNames addObject:FLXPostgresParameterTimeZone]; + [_parameterNames addObject:FLXPostgresParameterIntegerDateTimes]; + } + + pthread_mutex_unlock(&_readLock); + + [self performSelectorInBackground:@selector(_loadParameters:) withObject:_parameterNames]; + + return YES; +} + +/** + * Gets the object for the supplied parameter. + * + * @param parameter The name of the parameter to lookup. + * + * @return The parameter value or nil if parameters haven't been loaded or it can't be found. + */ +- (id)valueForParameter:(NSString *)parameter +{ + pthread_mutex_lock(&_readLock); + + if (!_parameters || ![_parameters count]) { + pthread_mutex_unlock(&_readLock); + + return nil; + } + + id value = [_parameters objectForKey:parameter]; + + pthread_mutex_unlock(&_readLock); + + return value; +} + +#pragma mark - +#pragma mark Private API + +/** + * Loads the values of the supplied array of parameters by query the current connection. + * + * @param object The parameters to load. + */ +- (void)_loadParameters:(id)object +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + pthread_mutex_lock(&_readLock); + + NSArray *parameters = (NSArray *)object; + + if (!_parameters) { + _parameters = [[NSMutableDictionary alloc] initWithCapacity:[parameters count]]; + } + + for (NSString *parameter in parameters) + { + const char *value = PQparameterStatus([_connection postgresConnection], [parameter UTF8String]); + + if (!value) continue; + + NSString *stringValue = [NSString stringWithUTF8String:value]; + + id paramObject = [self _isBooleanParameterValue:stringValue] ? [NSNumber numberWithBool:[self _booleanForParameterValue:stringValue]] : stringValue; + + [_parameters setObject:paramObject forKey:parameter]; + } + + pthread_mutex_unlock(&_readLock); + + [pool release]; +} + +/** + * Determines whether or not the supplied value is a boolean value. + * + * @param value The value to look at. + * + * @return A BOOL indicating if the value is a boolean type. + */ +- (BOOL)_isBooleanParameterValue:(NSString *)value +{ + value = [value uppercaseString]; + + return + [value isEqualToString:@"ON"] || + [value isEqualToString:@"YES"] || + [value isEqualToString:@"TRUE"] || + [value isEqualToString:@"OFF"] || + [value isEqualToString:@"NO"] || + [value isEqualToString:@"FALSE"]; +} + +/** + * Determines the boolean value for the supplied boolean string representation. + * + * @param value The value to look at. + * + * @return A BOOL indicating the value of the string representation. + */ +- (BOOL)_booleanForParameterValue:(NSString *)value +{ + value = [value uppercaseString]; + + return + [value isEqualToString:@"ON"] || + [value isEqualToString:@"YES"] || + [value isEqualToString:@"TRUE"]; +} + +#pragma mark - + +- (void)dealloc +{ + _connection = nil; + + pthread_mutex_destroy(&_readLock); + + if (_parameters) [_parameters release], _parameters = nil; + if (_parameterNames) [_parameterNames release], _parameterNames = nil; + + [super dealloc]; +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionPrivateAPI.h b/Frameworks/PostgresKit/Source/FLXPostgresConnectionPrivateAPI.h new file mode 100644 index 00000000..53bcf4ee --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionPrivateAPI.h @@ -0,0 +1,38 @@ +// +// $Id$ +// +// FLXPostgresConnectionPrivateAPI.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresConnection.h" + +@interface FLXPostgresConnection (FLXPostgresConnectionPrivateAPI) + +- (PGconn *)postgresConnection; +- (void)_createConnectionParameters; + +@end + +@interface FLXPostgresConnection (FLXPostgresConnectionQueryPreparationPrivateAPI) + +- (BOOL)_prepare:(FLXPostgresStatement *)statement num:(int)paramNum types:(FLXPostgresOid *)paramTypes; + +@end + + diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionPrivateAPI.m b/Frameworks/PostgresKit/Source/FLXPostgresConnectionPrivateAPI.m new file mode 100644 index 00000000..52610305 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionPrivateAPI.m @@ -0,0 +1,29 @@ +// +// FLXPostgresConnectionPrivateAPI.m +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresConnectionPrivateAPI.h" +#import "FLXPostgresConnectionDelegateProtocol.h" +#import "FLXPostgresResult.h" + +@implementation FLXPostgresConnection (FLXPostgresConnectionPrivateAPI) + + + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryExecution.h b/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryExecution.h new file mode 100644 index 00000000..b1c8aed0 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryExecution.h @@ -0,0 +1,38 @@ +// +// $Id$ +// +// FLXPostgresConnectionQueryExecution.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresConnection.h" + +@interface FLXPostgresConnection (FLXPostgresConnectionQueryExecution) + +// Synchronous interface +- (FLXPostgresResult *)execute:(NSString *)query; +- (FLXPostgresResult *)executeWithFormat:(NSString *)query, ...; +- (FLXPostgresResult *)executePrepared:(FLXPostgresStatement *)statement; +- (FLXPostgresResult *)execute:(NSString *)query values:(NSArray *)values; +- (FLXPostgresResult *)execute:(NSString *)query value:(NSObject *)value; +- (FLXPostgresResult *)executePrepared:(FLXPostgresStatement *)statement values:(NSArray *)values; +- (FLXPostgresResult *)executePrepared:(FLXPostgresStatement *)statement value:(NSObject *)value; + +// Asynchronous interface + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryExecution.m b/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryExecution.m new file mode 100644 index 00000000..894338a7 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryExecution.m @@ -0,0 +1,322 @@ +// +// $Id$ +// +// FLXPostgresConnectionQueryExecution.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresConnectionQueryExecution.h" +#import "FLXPostgresConnectionPrivateAPI.h" +#import "FLXPostgresConnectionTypeHandling.h" +#import "FLXPostgresConnectionDelegate.h" +#import "FLXPostgresConnection.h" +#import "FLXPostgresException.h" +#import "FLXPostgresResult.h" +#import "FLXPostgresTypeHandlerProtocol.h" +#import "FLXPostgresStatement.h" + +// Constants +static int FLXPostgresResultsAsBinary = 1; + +// Internal query structure +typedef struct +{ + int paramNum; + const void **paramValues; + FLXPostgresOid* paramTypes; + int *paramLengths; + int *paramFormats; +} +FLXQueryParamData; + +@interface FLXPostgresConnection () + +- (FLXPostgresResult *)_execute:(NSObject *)query values:(NSArray *)values; +- (BOOL)_queryDidError:(PGresult *)result; +- (FLXQueryParamData *)_createParameterDataStructureWithCount:(int)paramNum; +- (void)_destroyParamDataStructure:(FLXQueryParamData *)paramData; + +@end + +@implementation FLXPostgresConnection (FLXPostgresConnectionQueryExecution) + +#pragma mark - +#pragma mark Synchronous Interface + +- (FLXPostgresResult *)execute:(NSString *)query +{ + return [self _execute:query values:nil]; +} + +- (FLXPostgresResult *)execute:(NSString *)query value:(NSObject *)value +{ + return [self _execute:query values:[NSArray arrayWithObject:value]]; +} + +- (FLXPostgresResult *)executePrepared:(FLXPostgresStatement *)statement value:(NSObject *)value +{ + return [self _execute:statement values:[NSArray arrayWithObject:value]]; +} + +- (FLXPostgresResult *)executePrepared:(FLXPostgresStatement *)statement +{ + return [self _execute:statement values:nil]; +} + +- (FLXPostgresResult *)executePrepared:(FLXPostgresStatement *)statement values:(NSArray *)values +{ + return [self _execute:statement values:values]; +} + +- (FLXPostgresResult *)execute:(NSString *)query values:(NSArray *)values +{ + return [self _execute:query values:values]; +} + +- (FLXPostgresResult *)executeWithFormat:(NSString *)query, ... +{ + va_list argumentList; + va_start(argumentList, query); + + NSMutableString *string = [[NSMutableString alloc] init]; + + CFStringAppendFormatAndArguments((CFMutableStringRef)string, (CFDictionaryRef)nil, (CFStringRef)query, argumentList); + + va_end(argumentList); + + FLXPostgresResult *result = [self _execute:string values:nil]; + + [string release]; + + return result; +} + +#pragma mark - +#pragma mark Asynchronous Interface + +#pragma mark - +#pragma mark Private API + +- (FLXPostgresResult *)_execute:(NSObject *)query values:(NSArray *)values +{ + _lastQueryWasCancelled = NO; + + if (![self isConnected] || !query || ![query isKindOfClass:[NSString class]] || [query isKindOfClass:[FLXPostgresStatement class]]) return nil; + + // Notify the delegate + if (_delegate && _delegateSupportsWillExecute) { + [_delegate connection:self willExecute:query values:values]; + } + + FLXQueryParamData *paramData = [self _createParameterDataStructureWithCount:values ? (int)[values count] : 0]; + + if (!paramData) return nil; + + // Fill the data structures + for (int i = 0; i < paramData->paramNum; i++) + { + id nativeObject = [values objectAtIndex:i]; + + NSParameterAssert(nativeObject); + + // Deterime if bound value is an NSNull + if ([nativeObject isKindOfClass:[NSNull class]]) { + paramData->paramValues[i] = NULL; + paramData->paramTypes[i] = 0; + paramData->paramLengths[i] = 0; + paramData->paramFormats[i] = 0; + + continue; + } + + // Obtain correct handler for this class + id typeHandler = [self typeHandlerForClass:[nativeObject class]]; + + if (!typeHandler) { + [self _destroyParamDataStructure:paramData]; + + // TODO: get rid of exceptions + [FLXPostgresException raise:FLXPostgresConnectionErrorDomain reason:[NSString stringWithFormat:@"Parameter $%u unsupported class %@", (i + 1), NSStringFromClass([nativeObject class])]]; + return nil; + } + + FLXPostgresOid type = 0; + NSData *data = [typeHandler remoteDataFromObject:nativeObject type:&type]; + + if (!data) { + [self _destroyParamDataStructure:paramData]; + + // TODO: get rid of exceptions + [FLXPostgresException raise:FLXPostgresConnectionErrorDomain reason:[NSString stringWithFormat:@"Parameter $%u cannot be converted into a bound value", (i + 1)]]; + return nil; + } + + // Check length of data + if ([data length] > INT_MAX) { + [self _destroyParamDataStructure:paramData]; + + // TODO: get rid of exceptions + [FLXPostgresException raise:FLXPostgresConnectionErrorDomain reason:[NSString stringWithFormat:@"Bound value $%u exceeds maximum size", (i + 1)]]; + return nil; + } + + // Assign data + paramData->paramTypes[i] = type; + + // NOTE: if data length is zero, we encode as text instead, as NSData returns 0 for + // empty data, and it gets encoded as a NULL. + if ([data length] == 0) { + paramData->paramValues[i] = ""; + paramData->paramFormats[i] = 0; + paramData->paramLengths[i] = 0; + } + else { + // Send as binary data + paramData->paramValues[i] = [data bytes]; + paramData->paramLengths[i] = (int)[data length]; + paramData->paramFormats[i] = 1; + } + } + + // Execute the command - return data in binary + PGresult *result = nil; + + if ([query isKindOfClass:[NSString class]]) { + + result = PQexecParams(_connection, + [(NSString *)query UTF8String], + paramData->paramNum, + paramData->paramTypes, + (const char **)paramData->paramValues, + (const int *)paramData->paramLengths, + (const int *)paramData->paramFormats, + FLXPostgresResultsAsBinary); + } + else if ([query isKindOfClass:[FLXPostgresStatement class]]) { + FLXPostgresStatement *statement = (FLXPostgresStatement *)query; + + // Statement has not been prepared yet, so prepare it with the given parameter types + if (![statement name]) { + BOOL prepareResult = [self _prepare:statement num:paramData->paramNum types:paramData->paramTypes]; + + if (!prepareResult || ![statement name]) return nil; + } + + result = PQexecPrepared(_connection, + [statement UTF8Name], + paramData->paramNum, + (const char **)paramData->paramValues, + (const int *)paramData->paramLengths, + (const int *)paramData->paramFormats, + FLXPostgresResultsAsBinary); + } + else { + // TODO: get rid of exceptions + [FLXPostgresException raise:FLXPostgresConnectionErrorDomain reason:[NSString stringWithFormat:@"Trying to execute a query that is not of type NSString or FLXPostgresStatement"]]; + return nil; + } + + [self _destroyParamDataStructure:paramData]; + + if (!result || [self _queryDidError:result]) return nil; + + return [[[FLXPostgresResult alloc] initWithResult:result connection:self] autorelease]; +} + +/** + * Determines whether or not the supplied result indicates an error occurred. + * + * @param result The result to examine. + * + * @return A BOOL indicating if an error occurred. + */ +- (BOOL)_queryDidError:(PGresult *)result +{ + ExecStatusType status = PQresultStatus(result); + + if (status == PGRES_BAD_RESPONSE || status == PGRES_FATAL_ERROR) { + NSString *error = [NSString stringWithUTF8String:PQresultErrorMessage(result)]; + + if (_lastErrorMessage) [_lastErrorMessage release], _lastErrorMessage = nil; + + _lastErrorMessage = [[NSString alloc] initWithString:error]; + + PQclear(result); + + return YES; + } + + return NO; +} + +/** + * Creates the internal query parameter data structure. + * + * @note This method will throw an exception if it can't allocated the required memory. + * + * @param paramNum The number of parameters the structure should accommodate. + * + * @return The data structure or nil if an exception occurred. + */ +- (FLXQueryParamData *)_createParameterDataStructureWithCount:(int)paramNum +{ + FLXQueryParamData *paramData = malloc(sizeof(FLXQueryParamData)); + + paramData->paramNum = paramNum; + paramData->paramValues = NULL; + paramData->paramTypes = NULL; + paramData->paramLengths = NULL; + paramData->paramFormats = NULL; + + if (paramData->paramNum) { + paramData->paramValues = malloc(sizeof(void *) * paramData->paramNum); + paramData->paramTypes = malloc(sizeof(FLXPostgresOid) * paramData->paramNum); + paramData->paramLengths = malloc(sizeof(int) * paramData->paramNum); + paramData->paramFormats = malloc(sizeof(int) * paramData->paramNum); + + if (!paramData->paramValues || !paramData->paramLengths || !paramData->paramFormats) { + [self _destroyParamDataStructure:paramData]; + + // Probably justifies throwing an exception if we can't allocate any memory! + [FLXPostgresException raise:FLXPostgresConnectionErrorDomain reason:@"Memory allocation error"]; + + return nil; + } + } + + return paramData; +} + +/** + * Frees the memory associated with the supplied parameter data structure. + * + * @param paramData The parameter data to destroy. + */ +- (void)_destroyParamDataStructure:(FLXQueryParamData *)paramData +{ + if (!paramData) return; + + if (paramData->paramValues) free(paramData->paramValues); + if (paramData->paramTypes) free(paramData->paramTypes); + if (paramData->paramLengths) free(paramData->paramLengths); + if (paramData->paramFormats) free(paramData->paramFormats); + + free(paramData); +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryPreparation.h b/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryPreparation.h new file mode 100644 index 00000000..1d769b51 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryPreparation.h @@ -0,0 +1,32 @@ +// +// $Id$ +// +// FLXPostgresConnectionQueryPreparation.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresConnection.h" + +@interface FLXPostgresConnection (FLXPostgresConnectionQueryPreparation) + +- (NSString *)quote:(NSObject *)object; + +- (FLXPostgresStatement *)prepare:(NSString *)query; +- (FLXPostgresStatement *)prepareWithFormat:(NSString *)query, ...; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryPreparation.m b/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryPreparation.m new file mode 100644 index 00000000..d8bf59b7 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionQueryPreparation.m @@ -0,0 +1,131 @@ +// +// $Id$ +// +// FLXPostgresConnectionQueryPreparation.m +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresConnectionQueryPreparation.h" +#import "FLXPostgresConnectionTypeHandling.h" +#import "FLXPostgresConnectionPrivateAPI.h" +#import "FLXPostgresStatement.h" +#import "FLXPostgresException.h" + +@implementation FLXPostgresConnection (FLXPostgresConnectionQueryPreparation) + +/** + * Quotes the supplied object in accordance with it's data type. + * + * @param object The object to quote. + * + * @return A string representation of the quoted object. + */ +- (NSString *)quote:(NSObject *)object +{ + if (!object || [object isKindOfClass:[NSNull class]]) return @"NULL"; + + id handler = [self typeHandlerForClass:[object class]]; + + if (!handler) { + [FLXPostgresException raise:FLXPostgresConnectionErrorDomain + reason:[NSString stringWithFormat:@"Unsupported class '%@'", NSStringFromClass([object class])]]; + + return nil; + } + + return [handler quotedStringFromObject:object]; +} + +/** + * Creates a prepared statment from the supplied query. + * + * @param query The query to create the statement from. + * + * @return The prepared statement instance. + */ +- (FLXPostgresStatement *)prepare:(NSString *)query +{ + if (!query || ![query length]) return nil; + + return [[[FLXPostgresStatement alloc] initWithStatement:query] autorelease]; +} + +/** + * Creates a prepared statment from the supplied query format and values. + * + * @param query The query to create the statement from. + * @param ... Any values to insert into the query (optional). + * + * @return The prepared statement instance. + */ +- (FLXPostgresStatement *)prepareWithFormat:(NSString *)query, ... +{ + if (!query || ![query length]) return nil; + + va_list args; + va_start(args, query); + + NSMutableString *string = [[NSMutableString alloc] initWithFormat:query arguments:args]; + + va_end(args); + + FLXPostgresStatement *statement = [self prepare:string]; + + [string release]; + + return statement; +} + +#pragma mark - +#pragma mark Private API + +/** + * Actually prepares the supplied statement against the database. + * + * @param statement The statement to prepare. + * @param paranNum The number of parameters the statement contains. + * @param paramTypes Any of Postgres parameter types. + * + * @return A BOOL indicating succes. Returns NO if there's no statement, statement name or current connection. + */ +- (BOOL)_prepare:(FLXPostgresStatement *)statement num:(int)paramNum types:(FLXPostgresOid *)paramTypes +{ + if (!statement || ![statement name] || ![self isConnected]) return NO; + + NSString *name = [[NSProcessInfo processInfo] globallyUniqueString]; + + PGresult *result = PQprepare(_connection, [name UTF8String], [statement UTF8Statement], paramNum, paramTypes); + + if (!result) return NO; + + ExecStatusType resultStatus = PQresultStatus(result); + + if (resultStatus == PGRES_BAD_RESPONSE || resultStatus == PGRES_FATAL_ERROR) { + PQclear(result); + + return NO; + } + + PQclear(result); + + [statement setName:name]; + + return YES; +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionTypeHandling.h b/Frameworks/PostgresKit/Source/FLXPostgresConnectionTypeHandling.h new file mode 100644 index 00000000..351b1d24 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionTypeHandling.h @@ -0,0 +1,33 @@ +// +// $Id$ +// +// FLXPostgresConnectionTypeHandling.h +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on July 29, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresConnection.h" + +@interface FLXPostgresConnection (FLXPostgresConnectionTypeHandling) + +- (void)registerTypeHandlers; + +- (id )typeHandlerForClass:(Class)class; +- (id )typeHandlerForRemoteType:(FLXPostgresOid)type; + +- (void)registerTypeHandler:(Class)handlerClass; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionTypeHandling.m b/Frameworks/PostgresKit/Source/FLXPostgresConnectionTypeHandling.m new file mode 100644 index 00000000..bd46e788 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionTypeHandling.m @@ -0,0 +1,105 @@ +// +// $Id$ +// +// FLXPostgresConnectionTypeHandling.m +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on July 29, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresConnectionTypeHandling.h" +#import "FLXPostgresTypeStringHandler.h" +#import "FLXPostgresTypeNumberHandler.h" +#import "FLXPostgresException.h" + +@implementation FLXPostgresConnection (FLXPostgresConnectionTypeHandling) + +/** + * Register all of our data type handlers for this connection. + */ +- (void)registerTypeHandlers +{ + if (_typeMap) { + [_typeMap release]; + + _typeMap = [[NSMutableDictionary alloc] init]; + } + + [self registerTypeHandler:[FLXPostgresTypeStringHandler class]]; + [self registerTypeHandler:[FLXPostgresTypeNumberHandler class]]; +} + +/** + * Get the data type handler for the supplied class. + * + * @param class The class to get the handler for. + * + * @return The handler or nil if there's none associated with the class. + */ +- (id )typeHandlerForClass:(Class)class +{ + return [_typeMap objectForKey:NSStringFromClass(class)]; +} + +/** + * Get the data type handler for the supplied PostgreSQL type. + * + * @param type The PostgreSQL type to get the handler for. + * + * @return The handler or nil if there's none associated with the type. + */ +- (id )typeHandlerForRemoteType:(FLXPostgresOid)type +{ + return [_typeMap objectForKey:[NSNumber numberWithUnsignedInteger:type]]; +} + +/** + * Register the supplied type handler class. + * + * @param handlerClass The handler class to register. + */ +- (void)registerTypeHandler:(Class)handlerClass +{ + if (![handlerClass conformsToProtocol:@protocol(FLXPostgresTypeHandlerProtocol)]) { + [FLXPostgresException raise:FLXPostgresConnectionErrorDomain + reason:@"Class '%@' does not conform to protocol '%@'", NSStringFromClass(handlerClass), NSStringFromProtocol(@protocol(FLXPostgresTypeHandlerProtocol))]; + } + + // Create an instance of this class + id handler = [[[handlerClass alloc] initWithConnection:self] autorelease]; + + // Add to the type map - for native class + [_typeMap setObject:handler forKey:NSStringFromClass([handler nativeClass])]; + + NSArray *aliases = [handler classAliases]; + + if (aliases) { + for (NSString *alias in aliases) + { + [_typeMap setObject:handler forKey:alias]; + } + } + + FLXPostgresOid *remoteTypes = [handler remoteTypes]; + + for (NSUInteger i = 0; remoteTypes[i]; i++) + { + NSNumber *key = [NSNumber numberWithUnsignedInteger:remoteTypes[i]]; + + [_typeMap setObject:handler forKey:key]; + } +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionUtils.h b/Frameworks/PostgresKit/Source/FLXPostgresConnectionUtils.h new file mode 100644 index 00000000..60eacfea --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionUtils.h @@ -0,0 +1,33 @@ +// +// $Id$ +// +// FLXPostgresConnectionUtils.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresConnection.h" + +@interface FLXPostgresConnection (FLXPostgresConnectionUtils) + +- (NSArray *)schemas; +- (NSArray *)databases; +- (NSArray *)tablesInSchema:(NSString *)schema; +- (NSArray *)columnNamesForTable:(NSString *)table inSchema:(NSString *)schema; +- (NSString *)primaryKeyForTable:(NSString *)table inSchema:(NSString *)schema; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresConnectionUtils.m b/Frameworks/PostgresKit/Source/FLXPostgresConnectionUtils.m new file mode 100644 index 00000000..e988c7f2 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresConnectionUtils.m @@ -0,0 +1,133 @@ +// +// $Id$ +// +// FLXPostgresConnectionUtils.m +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresConnectionQueryPreparation.h" +#import "FLXPostgresConnectionQueryExecution.h" +#import "FLXPostgresResult.h" + +@interface FLXPostgresConnection () + +- (NSArray *)_executeAndReturnResult:(NSString *)query; + +@end + +@implementation FLXPostgresConnection (FLXPostgresConnectionUtils) + +/** + * Returns an array of all databases. + * + * @return An array of strings or nil if no connection is present. + */ +- (NSArray *)databases +{ + return [self isConnected] ? [self _executeAndReturnResult:@"SELECT DISTINCT \"catalog_name\" FROM \"information_schema\".\"schemata\""] : nil; +} + +/** + * Returns an array of all schemas. + * + * @return An array of strings or nil if no connection is present. + */ +- (NSArray *)schemas +{ + return [self isConnected] ? [self _executeAndReturnResult:[NSString stringWithFormat:@"SELECT \"schema_name\" FROM \"information_schema\".\"schemata\" WHERE \"catalog_name\" = %@", [self quote:[self database]]]] : nil; +} + +/** + * Returns an array of tables in the supplied schema. + * + * @param schem The schema to get tables for. + * + * @return An array of strings or nil if not connected or parameters are not valid. + */ +- (NSArray * )tablesInSchema:(NSString *)schema +{ + if (![self isConnected] || !schema || ![schema length]) return nil; + + return [self _executeAndReturnResult:[NSString stringWithFormat:@"SELECT \"table_name\" FROM \"information_schema\".\"tables\" WHERE \"table_catalog\" = %@ AND \"table_schema\" = %@ AND \"table_type\" = 'BASE TABLE'", [self quote:[self database]], [self quote:schema]]]; +} + +/** + * Get the primary key column name on the supplied table in the supplied schema. + * + * @param table The table to get the primary key for. + * @param schema The schem the table belongs to. + * + * @return The column name as a string or nil not connected or parameters are not valid. + */ +- (NSString *)primaryKeyForTable:(NSString *)table inSchema:(NSString *)schema +{ + if (![self isConnected] || !table || ![table length] || !schema || ![schema length]) return nil; + + NSString *join = @"\"information_schema\".\"table_constraints\" t INNER JOIN \"information_schema\".\"key_column_usage\" k ON t.\"constraint_name\" = k.\"constraint_name\""; + NSString *where = [NSString stringWithFormat:@"t.\"constraint_type\" = 'PRIMARY KEY' AND t.\"table_catalog\" = %@ AND t.\"table_schema\" = %@ AND t.\"table_name\" = %@", [self quote:[self database]], [self quote:schema], [self quote:table]]; + + FLXPostgresResult *result = [self executeWithFormat:@"SELECT k.\"column_name\" FROM %@ WHERE %@", join, where]; + + return [result numberOfRows] == 0 ? nil : [[result rowAsArray] objectAtIndex:0]; +} + +/** + * Returns an array of column names for the supplied table and schema. + * + * @param table The table to get column names from. + * @param schema The schem the table belongs to. + * + * @return An array of strings or nil if not connected or parameters are not valid. + */ +- (NSArray *)columnNamesForTable:(NSString *)table inSchema:(NSString *)schema +{ + if (![self isConnected] || !table || ![table length] || !schema || ![schema length]) return nil; + + return [self _executeAndReturnResult:[NSString stringWithFormat:@"SELECT \"column_name\" FROM \"information_schema\".\"columns\" WHERE \"table_catalog\" = %@ AND \"table_schema\" = %@ AND \"table_name\" = %@", [self quote:[self database]], [self quote:schema], [self quote:table]]]; +} + +#pragma mark - +#pragma mark Private API + +/** + * Executes the supplied query and returns the result. + * + * @param query The query to execute. + * + * @return The result as an array. + */ +- (NSArray *)_executeAndReturnResult:(NSString *)query +{ + FLXPostgresResult *result = [self execute:query]; + + if (!result || ![result numberOfRows]) return nil; + + NSArray *row = nil; + NSMutableArray *data = [NSMutableArray arrayWithCapacity:(NSUInteger)[result numberOfRows]]; + + while ((row = [result rowAsArray])) + { + if (![row count]) continue; + + [data addObject:[row objectAtIndex:0]]; + } + + return data; +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresError.h b/Frameworks/PostgresKit/Source/FLXPostgresError.h new file mode 100644 index 00000000..e9009ecb --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresError.h @@ -0,0 +1,74 @@ +// +// $Id$ +// +// FLXPostgresError.h +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on September 3, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +@interface FLXPostgresError : NSObject +{ + NSString *_errorSeverity; + NSString *_errorStateCode; + NSString *_errorPrimaryMessage; + NSString *_errorDetailMessage; + NSString *_errorMessageHint; + + NSUInteger _errorStatementPosition; +} + +/** + * @property errorSeverity + */ +@property (readonly) NSString *errorSeverity; + +/** + * @property errorStateCode + */ +@property (readonly) NSString *errorStateCode; + +/** + * @property errorPrimaryMessage + */ +@property (readonly) NSString *errorPrimaryMessage; + +/** + * @property errorDetailMessage + */ +@property (readonly) NSString *errorDetailMessage; + +/** + * @property errorMessageHint + */ +@property (readonly) NSString *errorMessageHint; + +/** + * @property errorStatementPosition + */ +@property (readonly) NSUInteger errorStatementPosition; + +- (id)initWithResult:(PGresult *)result; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresError.m b/Frameworks/PostgresKit/Source/FLXPostgresError.m new file mode 100644 index 00000000..6a0a6210 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresError.m @@ -0,0 +1,136 @@ +// +// $Id$ +// +// FLXPostgresError.m +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on September 3, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +#import "FLXPostgresError.h" +#import "FLXPostgresException.h" + +@interface FLXPostgresError () + +- (void)_extractErrorDetailsFromResult:(PGresult *)result; +- (NSString *)_extractErrorField:(int)field fromResult:(PGresult *)result; + +@end + +@implementation FLXPostgresError + +@synthesize errorSeverity = _errorSeverity; +@synthesize errorStateCode = _errorStateCode; +@synthesize errorPrimaryMessage = _errorPrimaryMessage; +@synthesize errorDetailMessage = _errorDetailMessage; +@synthesize errorMessageHint = _errorMessageHint; +@synthesize errorStatementPosition = _errorStatementPosition; + +#pragma mark - + +- (id)init +{ + [FLXPostgresException raise:NSInternalInconsistencyException + reason:@"%@ shouldn't be init'd directly; use initWithResult: instead.", [self className]]; + + return nil; +} + +- (id)initWithResult:(PGresult *)result +{ + if ((self = [super init])) { + + _errorSeverity = nil; + _errorStateCode = nil; + _errorPrimaryMessage = nil; + _errorDetailMessage = nil; + _errorMessageHint = nil; + _errorStatementPosition = -1; + + if (result) [self _extractErrorDetailsFromResult:result]; + } + + return self; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: Sev %@ (%@): %@>", [self className], _errorSeverity, _errorStateCode, _errorPrimaryMessage]; +} + +#pragma mark - +#pragma mark Private API + +/** + * Extracts all the error information from the supplied result. + * + * @param result The Postgres result to extract the information from. + */ +- (void)_extractErrorDetailsFromResult:(PGresult *)result +{ + // Note that we don't expose all the fields that are available. + // The ones we don't mostly include information internal to Postgres + // that generally isn't useful to end users. + _errorSeverity = [self _extractErrorField:PG_DIAG_SEVERITY fromResult:result]; + _errorStateCode = [self _extractErrorField:PG_DIAG_SQLSTATE fromResult:result]; + _errorPrimaryMessage = [self _extractErrorField:PG_DIAG_MESSAGE_PRIMARY fromResult:result]; + _errorDetailMessage = [self _extractErrorField:PG_DIAG_MESSAGE_DETAIL fromResult:result]; + _errorMessageHint = [self _extractErrorField:PG_DIAG_MESSAGE_HINT fromResult:result]; + + NSString *statementPosition = [self _extractErrorField:PG_DIAG_STATEMENT_POSITION fromResult:result]; + + _errorStatementPosition = [statementPosition integerValue]; + + [statementPosition release]; +} + +/** + * Extracts the supplied error field from the supplied Postgres result. + * + * @param field The error field to extract. + * @param result The Postgres result to extract the field from. + * + * @return A string representing the error value. The caller is responsible for freeing the associated memory. + */ +- (NSString *)_extractErrorField:(int)field fromResult:(PGresult *)result +{ + const char *errorData = PQresultErrorField(result, field); + + return [[NSString alloc] initWithBytes:errorData length:strlen(errorData) encoding:NSUTF8StringEncoding]; +} + +#pragma mark - + +- (void)dealloc +{ + if (_errorSeverity) [_errorSeverity release], _errorSeverity = nil; + if (_errorStateCode) [_errorStateCode release], _errorStateCode = nil; + if (_errorPrimaryMessage) [_errorPrimaryMessage release], _errorPrimaryMessage = nil; + if (_errorDetailMessage) [_errorDetailMessage release], _errorDetailMessage = nil; + if (_errorMessageHint) [_errorMessageHint release], _errorMessageHint = nil; + + [super dealloc]; +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresException.h b/Frameworks/PostgresKit/Source/FLXPostgresException.h new file mode 100644 index 00000000..54b781c4 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresException.h @@ -0,0 +1,28 @@ +// +// $Id$ +// +// FLXPostgresException.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +@interface FLXPostgresException : NSException + ++ (void)raise:(NSString *)name connection:(void *)connection; ++ (void)raise:(NSString *)name reason:(NSString *)reason, ...; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresException.m b/Frameworks/PostgresKit/Source/FLXPostgresException.m new file mode 100644 index 00000000..266ac1a7 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresException.m @@ -0,0 +1,64 @@ +// +// $Id$ +// +// FLXPostgresException.m +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresException.h" + +@implementation FLXPostgresException + +/** + * Raise a new exception with the supplied details. + * + * @param name The name of the exception to raise. + * @param connection The connection associated with the exception being raised. + */ ++ (void)raise:(NSString *)name connection:(void *)connection +{ + const char *errorMessage = "Unknown error"; + + if (connection) errorMessage = PQerrorMessage(connection); + + errorMessage = strlen(errorMessage) ? errorMessage : "Unknown error"; + + [[[[FLXPostgresException alloc] initWithName:name reason:[NSString stringWithUTF8String:errorMessage] userInfo:nil] autorelease] raise]; +} + +/** + * Raise a new exception with the supplied details. + * + * @param name The name of the exception to raise. + * @param reason The reason for the exception being raised. + */ ++ (void)raise:(NSString *)name reason:(NSString *)reason, ... +{ + va_list args; + va_start(args, reason); + + NSString *reasonMessage = [[NSString alloc] initWithFormat:reason arguments:args]; + + va_end(args); + + [[[[FLXPostgresException alloc] initWithName:name reason:reasonMessage userInfo:nil] autorelease] raise]; + + [reasonMessage release]; +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresResult.h b/Frameworks/PostgresKit/Source/FLXPostgresResult.h new file mode 100644 index 00000000..e198cee5 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresResult.h @@ -0,0 +1,71 @@ +// +// $Id$ +// +// FLXPostgresResult.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "libpq-fe.h" +#import "FLXConstants.h" + +@class FLXPostgresConnection; + +@interface FLXPostgresResult : NSObject +{ + void *_result; + void **_typeHandlers; + + unsigned long long _row; + unsigned int _numberOfFields; + unsigned long long _numberOfRows; + + NSString **_fields; + + NSStringEncoding _stringEncoding; + + FLXPostgresConnection *_connection; +} + +/** + * @property numberOfFields The number of fields this result has. + */ +@property (readonly) unsigned int numberOfFields; + +/** + * @property numberOfRows The number or rows this result has. + */ +@property (readonly) unsigned long long numberOfRows; + +/** + * @property stringEncoding The ecoding that was in use when this result was created. + */ +@property (readonly) NSStringEncoding stringEncoding; + +- (id)initWithResult:(PGresult *)result connection:(FLXPostgresConnection *)connection; + +- (unsigned int)numberOfFields; + +- (void)seekToRow:(unsigned long long)row; + +- (NSArray *)fields; + +- (NSArray *)rowAsArray; +- (NSDictionary *)rowAsDictionary; +- (id)rowAsType:(FLXPostgresResultRowType)type; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresResult.m b/Frameworks/PostgresKit/Source/FLXPostgresResult.m new file mode 100644 index 00000000..62e0d222 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresResult.m @@ -0,0 +1,264 @@ +// +// $Id$ +// +// FLXPostgresResult.m +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresResult.h" +#import "FLXPostgresException.h" +#import "FLXPostgresConnection.h" +#import "FLXPostgresConnectionTypeHandling.h" + +static NSString *FLXPostgresResultError = @"FLXPostgresResultError"; + +@interface FLXPostgresResult () + +- (void)_populateFields; +- (id)_objectForRow:(unsigned int)row column:(unsigned int)column; +- (id )_typeHandlerForColumn:(unsigned int)column; + +@end + +@implementation FLXPostgresResult + +@synthesize numberOfRows = _numberOfRows; +@synthesize numberOfFields = _numberOfFields; +@synthesize stringEncoding = _stringEncoding; + +#pragma mark - +#pragma mark Initialisation + +/** + * Prevent normal initialisation. + * + * @return nil + */ +- (id)init +{ + [FLXPostgresException raise:NSInternalInconsistencyException reason:@"%@ shouldn't be init'd directly; use initWithResult:connection: instead.", [self className]]; + + return nil; +} + +/** + * Initialises a result with the supplied details. + * + * @param result The underlying PostgreSQL result this wrapper represents. + * @param connection The connection the result came from. + * + * @return The result wrapper. + */ +- (id)initWithResult:(PGresult *)result connection:(FLXPostgresConnection *)connection +{ + NSParameterAssert(result); + + if ((self = [super init])) { + + _row = 0; + _result = result; + _numberOfRows = PQntuples(_result); + _numberOfFields = PQnfields(_result); + _connection = [connection retain]; + + _stringEncoding = [_connection stringEncoding]; + + _typeHandlers = (void **)calloc(sizeof(void *), _numberOfFields); + + unsigned long long affectedRows = (unsigned long long)[[NSString stringWithUTF8String:PQcmdTuples(_result)] longLongValue]; + + _numberOfRows = PQresultStatus(_result) == PGRES_TUPLES_OK ? _numberOfRows : affectedRows; + + [self _populateFields]; + } + + return self; +} + +#pragma mark - +#pragma mark Public API + +/** + * This result's fields as an array. + * + * @return The array of fields. + */ +- (NSArray *)fields +{ + return [NSArray arrayWithObjects:_fields count:_numberOfFields]; +} + +/** + * Sets the current row marker to the supplied row. + * + * @param row The row to seek to. + */ +- (void)seekToRow:(unsigned long long)row +{ + if (row >= _numberOfRows) row = _numberOfRows - 1; + + _row = row; +} + +#pragma mark - +#pragma mark Data Retrieval + +/** + * Return the current row as an array. + * + * @return The array of data. + */ +- (NSArray *)rowAsArray +{ + return [self rowAsType:FLXPostgresResultRowAsArray]; +} + +/** + * Return the current row as dictionary with keys as field names and values as the data. + * + * @return The row as a dictionary. + */ +- (NSDictionary *)rowAsDictionary +{ + return [self rowAsType:FLXPostgresResultRowAsDictionary]; +} + +/** + * Return the current row in the format specified by the supplied type. + * + * @return The data row as either an array or dictionary. + */ +- (id)rowAsType:(FLXPostgresResultRowType)type +{ + if (_row >= _numberOfRows) return nil; + + id data; + + data = (type == FLXPostgresResultRowAsArray) ? [NSMutableArray arrayWithCapacity:_numberOfFields] : [NSMutableDictionary dictionaryWithCapacity:_numberOfFields]; + + for (unsigned int i = 0; i < _numberOfFields; i++) + { + id object = [self _objectForRow:(int)_row column:i]; + + if (type == FLXPostgresResultRowAsArray) { + [(NSMutableArray *)data addObject:object]; + } + else { + [(NSMutableDictionary *)data setObject:object forKey:_fields[i]]; + } + } + + _row++; + + return data; +} + +#pragma mark - +#pragma mark Private API + +/** + * Populates the internal field names array. + */ +- (void)_populateFields +{ + _fields = malloc(sizeof(NSString *) * _numberOfFields); + + for (unsigned int i = 0; i < _numberOfFields; i++) + { + const char *bytes = PQfname(_result, i); + + if (!bytes) continue; + + _fields[i] = [[NSString alloc] initWithBytes:bytes length:strlen(bytes) encoding:_stringEncoding]; + } +} + +/** + * Get the native object at the supplied row and column. + * + * @param row The row index to get the data from. + * @param column The column index to get the data from. + * + * @return The native object or nil if out of this result's range. + */ +- (id)_objectForRow:(unsigned int)row column:(unsigned int)column +{ + if (row >= _numberOfRows || column >= _numberOfFields) return nil; + + // Check for null + if (PQgetisnull(_result, row, column)) return [NSNull null]; + + // Get bytes and length + const void *bytes = PQgetvalue(_result, row, column); + + NSUInteger length = PQgetlength(_result, row, column); + FLXPostgresOid type = PQftype(_result, column); + + // Get handler for this type + id handler = [self _typeHandlerForColumn:column]; + + if (!handler) { + NSLog(@"PostgresKit: Warning: No type handler found for type %d, return NSData.", type); + + return [NSData dataWithBytes:bytes length:length]; + } + + return [handler objectFromRemoteData:bytes length:length type:type]; +} + +/** + * Get the data type handler for the supplied column index. + * + * @param column The column index to get the handler for. + * + * @return The type handler or nil if out of this result's range. + */ +- (id )_typeHandlerForColumn:(unsigned int)column +{ + if (column >= _numberOfFields) return nil; + + id handler = _typeHandlers[column]; + + if (!handler) { + FLXPostgresOid type = PQftype(_result, column); + + _typeHandlers[column] = [_connection typeHandlerForRemoteType:type]; + + handler = _typeHandlers[column]; + } + + return handler; +} + +#pragma mark - + +-(void)dealloc +{ + PQclear(_result); + + for (unsigned int i = 0; i < _numberOfFields; i++) [_fields[i] release]; + + free(_fields); + free(_typeHandlers); + + if (_connection) [_connection release], _connection = nil; + + [super dealloc]; +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresStatement.h b/Frameworks/PostgresKit/Source/FLXPostgresStatement.h new file mode 100644 index 00000000..fafbb8dc --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresStatement.h @@ -0,0 +1,44 @@ +// +// $Id$ +// +// FLXPostgresStatement.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +@interface FLXPostgresStatement : NSObject +{ + NSString *_statement; + NSString *_name; +} + +/** + * @property statement The query statement. + */ +@property (readwrite, retain) NSString *statement; + +/** + * @property name The name of this statement. + */ +@property (readwrite, retain) NSString *name; + +- (id)initWithStatement:(NSString *)queryStatement; + +- (const char *)UTF8Name; +- (const char *)UTF8Statement; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresStatement.m b/Frameworks/PostgresKit/Source/FLXPostgresStatement.m new file mode 100644 index 00000000..a1505ccd --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresStatement.m @@ -0,0 +1,81 @@ +// +// $Id$ +// +// FLXPostgresStatement.m +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresStatement.h" + +@implementation FLXPostgresStatement + +@synthesize name = _name; +@synthesize statement = _statement; + +#pragma mark - +#pragma mark Initialisation + +- (id)initWithStatement:(NSString *)queryStatement +{ + if ((self = [super init])) { + [self setStatement:queryStatement]; + [self setName:nil]; + } + + return self; +} + +#pragma mark - +#pragma mark Public API + +/** + * Returns a null terminated C string of the statement's name. + * + * @return The statement name. + */ +- (const char *)UTF8Name +{ + return [[self name] UTF8String]; +} + +/** + * Returns a null terminated C string of the statement. + * + * @return The prepared statement. + */ +- (const char *)UTF8Statement +{ + return [[self statement] UTF8String]; +} + +- (NSString *)description +{ + return [self name] ? [NSString stringWithFormat:@"<%@ %@>", [self className], [self name]] : [NSString stringWithFormat:@"<%@>", [self className]]; +} + +#pragma mark - + +- (void)dealloc +{ + if (_name) [_name release], _name = nil; + if (_statement) [_statement release], _statement = nil; + + [super dealloc]; +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresTypeDateTimeHandler.h b/Frameworks/PostgresKit/Source/FLXPostgresTypeDateTimeHandler.h new file mode 100644 index 00000000..154d364f --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresTypeDateTimeHandler.h @@ -0,0 +1,35 @@ +// +// $Id$ +// +// FLXPostgresTypeDateTimeHandler.h +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on September 1, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +#import "FLXPostgresTypeHandler.h" + +@interface FLXPostgresTypeDateTimeHandler : FLXPostgresTypeHandler + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresTypeDateTimeHandler.m b/Frameworks/PostgresKit/Source/FLXPostgresTypeDateTimeHandler.m new file mode 100644 index 00000000..761e9e17 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresTypeDateTimeHandler.m @@ -0,0 +1,86 @@ +// +// $Id$ +// +// FLXPostgresTypeDateTimeHandler.m +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on September 1, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +#import "FLXPostgresTypeDateTimeHandler.h" +#import "FLXPostgresConnection.h" + +static FLXPostgresOid FLXPostgresTypeDateTimeTypes[] = +{ + FLXPostgresOidAbsTime, + FLXPostgresOidDate, + FLXPostgresOidTime, + FLXPostgresOidTimeTZ, + 0 +}; + +@implementation FLXPostgresTypeDateTimeHandler + +#pragma mark - +#pragma mark Protocol Implementation + +- (FLXPostgresOid *)remoteTypes +{ + return FLXPostgresTypeDateTimeTypes; +} + +- (Class)nativeClass +{ + return [NSDate class]; +} + +- (NSArray *)classAliases +{ + return nil; +} + +- (NSData *)remoteDataFromObject:(id)object type:(FLXPostgresOid *)type +{ + if (!object || !type || ![object isKindOfClass:[NSDate class]]) return nil; + + return nil; +} + +- (id)objectFromRemoteData:(const void *)bytes length:(NSUInteger)length type:(FLXPostgresOid)type +{ + if (!bytes || !type) return nil; + + // TODO: Imeplement me! + return nil; +} + +- (NSString *)quotedStringFromObject:(id)object +{ + if (!object || ![object isKindOfClass:[NSString class]]) return nil; + + // TODO: Imeplement me! + return nil; +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresTypeHandler.h b/Frameworks/PostgresKit/Source/FLXPostgresTypeHandler.h new file mode 100644 index 00000000..f2808d52 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresTypeHandler.h @@ -0,0 +1,38 @@ +// +// $Id$ +// +// FLXPostgresTypeHandler.h +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on July 27, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresTypeHandlerProtocol.h" + +@class FLXPostgresConnection; + +@interface FLXPostgresTypeHandler : NSObject +{ + FLXPostgresConnection *_connection; +} + +/** + * @property connection The connection this type handler is associated with. + */ +@property (readonly) FLXPostgresConnection *connection; + +- (id)initWithConnection:(FLXPostgresConnection *)connection; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresTypeHandler.m b/Frameworks/PostgresKit/Source/FLXPostgresTypeHandler.m new file mode 100644 index 00000000..a1b5fac6 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresTypeHandler.m @@ -0,0 +1,46 @@ +// +// $Id$ +// +// FLXPostgresTypeHandler.h +// PostgresKit +// +// Created by Stuart Connolly (stuconnolly.com) on July 27, 2012. +// Copyright (c) 2012 Stuart Connolly. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresTypeHandler.h" + +@implementation FLXPostgresTypeHandler + +@synthesize connection = _connection; + +- (id)initWithConnection:(FLXPostgresConnection *)connection +{ + if ((self = [super init])) { + _connection = [connection retain]; + } + + return self; +} + +#pragma mark - + +- (void)dealloc +{ + if (_connection) [_connection release], _connection = nil; + + [super dealloc]; +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresTypeHandlerProtocol.h b/Frameworks/PostgresKit/Source/FLXPostgresTypeHandlerProtocol.h new file mode 100644 index 00000000..49261e60 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresTypeHandlerProtocol.h @@ -0,0 +1,84 @@ +// +// $Id$ +// +// FLXPostgresTypeHandlerProtocol.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresTypes.h" + +@class FLXPostgresConnection; + +/** + * @protocol FLXPostgresTypeHandlerProtocol + */ +@protocol FLXPostgresTypeHandlerProtocol + +/** + * The remote type values handled by this class (terminated by 0). + * + * @return The remote types as an array of FLXPostgresOid's. + */ +- (FLXPostgresOid *)remoteTypes; + +/** + * What is the native class this class handles. + * + * @return The native class. + */ +- (Class)nativeClass; + +/** + * Any aliases that the native class is known by. + * + * @return An of aliases as strings or nil if none. + */ +- (NSArray *)classAliases; + +/** + * Return a transmittable data representation from the supplied object, + * and set the remote type for the data. + * + * @param object The object to produce the data for. + * @param type The type of object we're supplying. + * + * @return The data represenation as an NSData instance. + */ +- (NSData *)remoteDataFromObject:(id)object type:(FLXPostgresOid *)type; + +/** + * Convert the supplied remote data into an object. + * + * @param bytes The remote data to convert. + * @param length The length of the data. + * @param type The type of data. + * + * @return An object represenation of the data. + */ +- (id)objectFromRemoteData:(const void *)bytes length:(NSUInteger)length type:(FLXPostgresOid)type; + +/** + * Return a quoted string from an object. + * + * @param object The object to quote. + * + * @return A string represenation of the object quoted. + */ +- (NSString *)quotedStringFromObject:(id)object; + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresTypeNumberHandler.h b/Frameworks/PostgresKit/Source/FLXPostgresTypeNumberHandler.h new file mode 100644 index 00000000..a3305e19 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresTypeNumberHandler.h @@ -0,0 +1,28 @@ +// +// $Id$ +// +// FLXPostgresTypeNumberHandler.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresTypeHandler.h" + +@interface FLXPostgresTypeNumberHandler : FLXPostgresTypeHandler + +@end + diff --git a/Frameworks/PostgresKit/Source/FLXPostgresTypeNumberHandler.m b/Frameworks/PostgresKit/Source/FLXPostgresTypeNumberHandler.m new file mode 100644 index 00000000..0c75e282 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresTypeNumberHandler.m @@ -0,0 +1,325 @@ +// +// $Id$ +// +// FLXPostgresTypeNumberHandler.m +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresTypeNumberHandler.h" +#import "FLXPostgresTypes.h" + +static FLXPostgresOid FLXPostgresTypeNumberTypes[] = +{ + FLXPostgresOidInt8, + FLXPostgresOidInt2, + FLXPostgresOidInt4, + FLXPostgresOidFloat4, + FLXPostgresOidFloat8, + FLXPostgresOidBool, + 0 +}; + +@implementation FLXPostgresTypeNumberHandler + +#pragma mark - +#pragma mark Integer & Unsigned Integer + +- (SInt16)int16FromBytes:(const void *)bytes +{ + if (!bytes) return 0; + + return EndianS16_BtoN(*((SInt16 *)bytes)); +} + +- (SInt32)int32FromBytes:(const void *)bytes +{ + if (!bytes) return 0; + + return EndianS32_BtoN(*((SInt32 *)bytes)); +} + +- (SInt64)int64FromBytes:(const void *)bytes +{ + if (!bytes) return 0; + + return EndianS64_BtoN(*((SInt64 *)bytes)); +} + +- (UInt16)unsignedInt16FromBytes:(const void *)bytes +{ + if (!bytes) return 0; + + return EndianU16_BtoN(*((UInt16 *)bytes)); +} + +- (UInt32)unsignedInt32FromBytes:(const void *)bytes +{ + if (!bytes) return 0; + + return EndianU32_BtoN(*((UInt32 *)bytes)); +} + +- (UInt64)unsignedInt64FromBytes:(const void *)bytes +{ + if (!bytes) return 0; + + return EndianU64_BtoN(*((UInt64 *)bytes)); +} + +- (NSNumber *)integerObjectFromBytes:(const void *)bytes length:(NSUInteger)length +{ + if (!bytes) return nil; + + switch (length) + { + case 2: + return [NSNumber numberWithShort:[self int16FromBytes:bytes]]; + case 4: + return [NSNumber numberWithInteger:[self int32FromBytes:bytes]]; + case 8: + return [NSNumber numberWithLongLong:[self int64FromBytes:bytes]]; + } + + return nil; +} + +- (NSNumber *)unsignedIntegerObjectFromBytes:(const void *)bytes length:(NSUInteger)length +{ + if (!bytes) return nil; + + switch (length) + { + case 2: + return [NSNumber numberWithUnsignedShort:[self unsignedInt16FromBytes:bytes]]; + case 4: + return [NSNumber numberWithUnsignedInteger:[self unsignedInt32FromBytes:bytes]]; + case 8: + return [NSNumber numberWithUnsignedLongLong:[self unsignedInt64FromBytes:bytes]]; + } + + return nil; +} + +- (NSData *)remoteDataFromInt64:(SInt64)value +{ + if (sizeof(SInt64) != 8) return nil; + + value = EndianS64_NtoB(value); + + return [NSData dataWithBytes:&value length:sizeof(value)]; +} + +- (NSData *)remoteDataFromInt32:(SInt32)value +{ + if (sizeof(SInt32) != 4) return nil; + + value = EndianS32_NtoB(value); + + return [NSData dataWithBytes:&value length:sizeof(value)]; +} + +- (NSData *)remoteDataFromInt16:(SInt16)value +{ + if (sizeof(SInt16) != 2) return nil; + + value = EndianS16_NtoB(value); + + return [NSData dataWithBytes:&value length:sizeof(value)]; +} + +#pragma mark - +#pragma mark Floating Point + +- (Float32)float32FromBytes:(const void *)bytes +{ + if (!bytes) return 0; + + union { Float32 r; UInt32 i; } u32; + + u32.r = *((Float32 *)bytes); + u32.i = CFSwapInt32HostToBig(u32.i); + + return u32.r; +} + +- (Float64)float64FromBytes:(const void *)bytes +{ + if (!bytes) return 0; + + union { Float64 r; UInt64 i; } u64; + + u64.r = *((Float64 *)bytes); + u64.i = CFSwapInt64HostToBig(u64.i); + + return u64.r; +} + +- (NSNumber *)realObjectFromBytes:(const void *)bytes length:(NSUInteger)length +{ + switch (length) + { + case 4: + return [NSNumber numberWithFloat:[self float32FromBytes:bytes]]; + case 8: + return [NSNumber numberWithDouble:[self float64FromBytes:bytes]]; + } + + return nil; +} + + +- (NSData *)remoteDataFromFloat32:(Float32)value +{ + if (sizeof(Float32) != 4) return nil; + + union { Float32 r; UInt32 i; } u32; + + u32.r = value; + u32.i = CFSwapInt32HostToBig(u32.i); + + return [NSData dataWithBytes:&u32 length:sizeof(u32)]; +} + +- (NSData *)remoteDataFromFloat64:(Float64)value +{ + if (sizeof(Float64) != 8) return nil; + + union { Float64 r; UInt64 i; } u64; + + u64.r = value; + u64.i = CFSwapInt64HostToBig(u64.i); + + return [NSData dataWithBytes:&u64 length:sizeof(u64)]; +} + +#pragma mark - +#pragma mark Boolean + +- (BOOL)booleanFromBytes:(const void *)bytes +{ + if (!bytes) return NO; + + return (*((const int8_t *)bytes) ? YES : NO); +} + +- (NSNumber *)booleanObjectFromBytes:(const void *)bytes length:(NSUInteger)length +{ + if (!bytes || length != 1) return nil; + + return [NSNumber numberWithBool:[self booleanFromBytes:bytes]]; +} + + +- (NSData *)remoteDataFromBoolean:(BOOL)value +{ + return [NSData dataWithBytes:&value length:1]; +} + +#pragma mark - +#pragma mark Protocol Implementation + +- (FLXPostgresOid *)remoteTypes +{ + return FLXPostgresTypeNumberTypes; +} + +- (Class)nativeClass +{ + return [NSNumber class]; +} + +- (NSArray *)classAliases +{ + return nil; +} + +- (NSData *)remoteDataFromObject:(id)object type:(FLXPostgresOid *)type +{ + if (!object || !type || ![object isKindOfClass:[NSNumber class]]) return nil; + + NSNumber *number = (NSNumber *)object; + + const char *objectType = [number objCType]; + + switch (objectType[0]) + { + case 'c': + case 'C': + case 'B': // Boolean + (*type) = FLXPostgresOidBool; + return [self remoteDataFromBoolean:[(NSNumber *)object boolValue]]; + case 'i': // Integer + case 'l': // Long + case 'S': // Unsigned short + (*type) = FLXPostgresOidInt4; + return [self remoteDataFromInt32:[(NSNumber *)object shortValue]]; + case 's': + (*type) = FLXPostgresOidInt2; + return [self remoteDataFromInt16:[(NSNumber *)object shortValue]]; + case 'q': // Long long + case 'Q': // Unsigned long long + case 'I': // Unsigned integer + case 'L': // Unsigned long + (*type) = FLXPostgresOidInt8; + return [self remoteDataFromInt64:[(NSNumber *)object longLongValue]]; + case 'f': // Float + (*type) = FLXPostgresOidFloat4; + return [self remoteDataFromFloat32:[(NSNumber *)object floatValue]]; + case 'd': // Double + (*type) = FLXPostgresOidFloat8; + return [self remoteDataFromFloat64:[(NSNumber *)object doubleValue]]; + } + + return nil; +} + +- (id)objectFromRemoteData:(const void *)bytes length:(NSUInteger)length type:(FLXPostgresOid)type +{ + if (!bytes || !length || !type) return nil; + + switch (type) + { + case FLXPostgresOidInt8: + case FLXPostgresOidInt2: + case FLXPostgresOidInt4: + return [self integerObjectFromBytes:bytes length:length]; + case FLXPostgresOidFloat4: + case FLXPostgresOidFloat8: + return [self realObjectFromBytes:bytes length:length]; + case FLXPostgresOidBool: + return [self booleanObjectFromBytes:bytes length:length]; + default: + return nil; + } +} + +- (NSString *)quotedStringFromObject:(id)object +{ + if (!object || ![object isKindOfClass:[NSNumber class]]) return nil; + + const char *type = [object objCType]; + + if (type[0] == 'c' || type[0] == 'C' || type[0] == 'B') { + return ([(NSNumber *)object boolValue] ? @"true" : @"false"); + } + else { + return [(NSNumber *)object stringValue]; + } +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresTypeStringHandler.h b/Frameworks/PostgresKit/Source/FLXPostgresTypeStringHandler.h new file mode 100644 index 00000000..43a70cfe --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresTypeStringHandler.h @@ -0,0 +1,28 @@ +// +// $Id$ +// +// FLXPostgresTypeStringHandler.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresTypeHandler.h" + +@interface FLXPostgresTypeStringHandler : FLXPostgresTypeHandler + +@end + diff --git a/Frameworks/PostgresKit/Source/FLXPostgresTypeStringHandler.m b/Frameworks/PostgresKit/Source/FLXPostgresTypeStringHandler.m new file mode 100644 index 00000000..de1d064d --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresTypeStringHandler.m @@ -0,0 +1,77 @@ +// +// $Id$ +// +// FLXPostgresTypeStringHandler.m +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresTypeStringHandler.h" +#import "FLXPostgresConnection.h" +#import "FLXPostgresTypes.h" + +static FLXPostgresOid FLXPostgresTypeStringTypes[] = +{ + FLXPostgresOidText, + FLXPostgresOidChar, + FLXPostgresOidVarchar, + FLXPostgresOidUnknown, + 0 +}; + +@implementation FLXPostgresTypeStringHandler + +#pragma mark - +#pragma mark Protocol Implementation + +- (FLXPostgresOid *)remoteTypes +{ + return FLXPostgresTypeStringTypes; +} + +- (Class)nativeClass +{ + return [NSString class]; +} + +- (NSArray *)classAliases +{ + return [NSArray arrayWithObject:@"NSCFString"]; +} + +- (NSData *)remoteDataFromObject:(id)object type:(FLXPostgresOid *)type +{ + if (!object || !type || ![object isKindOfClass:[NSString class]]) return nil; + + return [(NSString *)object dataUsingEncoding:[_connection stringEncoding]]; +} + +- (id)objectFromRemoteData:(const void *)bytes length:(NSUInteger)length type:(FLXPostgresOid)type +{ + if (!bytes || !type) return nil; + + return [[[NSString alloc] initWithBytes:bytes length:length encoding:[_connection stringEncoding]] autorelease]; +} + +- (NSString *)quotedStringFromObject:(id)object +{ + if (!object || ![object isKindOfClass:[NSString class]]) return nil; + + return [NSString stringWithFormat:@"'%@'", [[object description] stringByReplacingOccurrencesOfString:@"'" withString:@"''"]]; +} + +@end diff --git a/Frameworks/PostgresKit/Source/FLXPostgresTypes.h b/Frameworks/PostgresKit/Source/FLXPostgresTypes.h new file mode 100644 index 00000000..7c146e41 --- /dev/null +++ b/Frameworks/PostgresKit/Source/FLXPostgresTypes.h @@ -0,0 +1,80 @@ +// +// $Id$ +// +// FLXPostgresTypes.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "postgres_ext.h" + +// Generic PostgreSQL object ID +typedef Oid FLXPostgresOid; + +// See PostgreSQL source: include/catalog/pg_type.h + +enum +{ + FLXPostgresOidBool = 16, + FLXPostgresOidData = 17, + FLXPostgresOidName = 19, + FLXPostgresOidInt8 = 20, + FLXPostgresOidInt2 = 21, + FLXPostgresOidInt4 = 23, + FLXPostgresOidText = 25, + FLXPostgresOidOid = 26, + FLXPostgresOidXML = 142, + FLXPostgresOidPoint = 600, + FLXPostgresOidLSeg = 601, + FLXPostgresOidPath = 602, + FLXPostgresOidBox = 603, + FLXPostgresOidPolygon = 604, + FLXPostgresOidFloat4 = 700, + FLXPostgresOidFloat8 = 701, + FLXPostgresOidAbsTime = 702, + FLXPostgresOidUnknown = 705, + FLXPostgresOidCircle = 718, + FLXPostgresOidMoney = 790, + FLXPostgresOidMacAddr = 829, + FLXPostgresOidIPAddr = 869, + FLXPostgresOidNetAddr = 869, + FLXPostgresOidArrayBool = 1000, + FLXPostgresOidArrayData = 1001, + FLXPostgresOidArrayChar = 1002, + FLXPostgresOidArrayName = 1003, + FLXPostgresOidArrayInt2 = 1005, + FLXPostgresOidArrayInt4 = 1007, + FLXPostgresOidArrayText = 1009, + FLXPostgresOidArrayVarchar = 1015, + FLXPostgresOidArrayInt8 = 1016, + FLXPostgresOidArrayFloat4 = 1021, + FLXPostgresOidArrayFloat8 = 1022, + FLXPostgresOidArrayMacAddr = 1040, + FLXPostgresOidArrayIPAddr = 1041, + FLXPostgresOidChar = 1042, + FLXPostgresOidVarchar = 1043, + FLXPostgresOidDate = 1082, + FLXPostgresOidTime = 1083, + FLXPostgresOidTimestamp = 1114, + FLXPostgresOidTimestampTZ = 1184, + FLXPostgresOidInterval = 1186, + FLXPostgresOidTimeTZ = 1266, + FLXPostgresOidBit = 1560, + FLXPostgresOidVarbit = 1562, + FLXPostgresOidNumeric = 1700, + FLXPostgresOidMax = 1700 +}; diff --git a/Frameworks/PostgresKit/Source/PostgresKit-Prefix.pch b/Frameworks/PostgresKit/Source/PostgresKit-Prefix.pch new file mode 100644 index 00000000..822e702a --- /dev/null +++ b/Frameworks/PostgresKit/Source/PostgresKit-Prefix.pch @@ -0,0 +1,31 @@ +// +// $Id$ +// +// PostgresKit-Prefix.pch +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#ifdef __OBJC__ + #import + + // PostgreSQL interface + #import "libpq-fe.h" + + // Framework constants + #import "FLXConstants.h" +#endif diff --git a/Frameworks/PostgresKit/Source/PostgresKit.h b/Frameworks/PostgresKit/Source/PostgresKit.h new file mode 100644 index 00000000..cc9c7f13 --- /dev/null +++ b/Frameworks/PostgresKit/Source/PostgresKit.h @@ -0,0 +1,27 @@ +// +// PostgresKit.h +// PostgresKit +// +// Copyright (c) 2008-2009 David Thorpe, djt@mutablelogic.com +// +// Forked by the Sequel Pro Team on July 22, 2012. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#import "FLXPostgresResult.h" +#import "FLXPostgresStatement.h" +#import "FLXPostgresException.h" +#import "FLXPostgresConnection.h" +#import "FLXPostgresConnectionUtils.h" +#import "FLXPostgresConnectionQueryExecution.h" +#import "FLXPostgresConnectionQueryPreparation.h" -- cgit v1.2.3