/* PGConnection.m Copyright (C) 2004-2005 SKYRIX Software AG and Helge Hess Author: Helge Hess (helge.hess@opengroupware.org) This file is part of the PostgreSQL72 Adaptor Library This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "PGConnection.h" #include "common.h" #include #if PG_MAJOR_VERSION >= 6 && PG_MINOR_VERSION > 3 # define NG_HAS_NOTICE_PROCESSOR 1 # define NG_HAS_BINARY_TUPLES 1 # define NG_HAS_FMOD 1 #endif #if PG_MAJOR_VERSION >= 7 && PG_MINOR_VERSION > 3 # define NG_SET_CLIENT_ENCODING 1 #endif @implementation PGConnection static BOOL debugOn = NO; - (id)initWithHostName:(NSString *)_host port:(NSString *)_port options:(NSString *)_options tty:(NSString *)_tty database:(NSString *)_dbname login:(NSString *)_login password:(NSString *)_pwd { if ((self = [self init])) { NSException *error; error = [self connectWithHostName:_host port:_port options:_options tty:_tty database:_dbname login:_login password:_pwd]; if (error != nil) { if (debugOn) NSLog(@"%s: could not connect: %@", __PRETTY_FUNCTION__, error); [self release]; return nil; } } return self; } - (void)dealloc { [self finish]; [super dealloc]; } /* support */ - (const char *)_cstrFromString:(NSString *)_s { // TODO: fix API, check what the API string encoding is return [_s cString]; } - (NSString *)_stringFromCString:(const char *)_cstr { return [NSString stringWithCString:_cstr]; } /* accessors */ - (BOOL)isValid { return self->_connection != NULL ? YES : NO; } /* errors */ - (NSException *)_makeConnectException:(const char *)_func { return [NSException exceptionWithName:@"PGConnectFailed" reason:[NSString stringWithCString:_func] userInfo:nil]; } /* connect operations */ - (void)_disconnect { if (self->_connection != NULL) [self finish]; } - (NSException *)startConnectWithInfo:(NSString *)_conninfo { [self _disconnect]; self->_connection = PQconnectStart([self _cstrFromString:_conninfo]); if (self->_connection == NULL) return [self _makeConnectException:__PRETTY_FUNCTION__]; return nil; } // TODO: add method for polling connect status - (NSException *)connectWithInfo:(NSString *)_conninfo { [self _disconnect]; self->_connection = PQconnectdb([self _cstrFromString:_conninfo]); if (self->_connection == NULL) return [self _makeConnectException:__PRETTY_FUNCTION__]; return nil; } - (NSException *)connectWithHostName:(NSString *)_host port:(NSString *)_port options:(NSString *)_options tty:(NSString *)_tty database:(NSString *)_dbname login:(NSString *)_login password:(NSString *)_pwd { [self _disconnect]; self->_connection = PQsetdbLogin([self _cstrFromString:_host], [self _cstrFromString:_port], [self _cstrFromString:_options], [self _cstrFromString:_tty], [self _cstrFromString:_dbname], [self _cstrFromString:_login], [self _cstrFromString:_pwd]); if (self->_connection == NULL) return [self _makeConnectException:__PRETTY_FUNCTION__]; return nil; } - (void)finish { if (self->_connection != NULL) { PQfinish(self->_connection); self->_connection = NULL; } } - (BOOL)isConnectionOK { if (![self isValid]) return NO; return PQstatus(self->_connection) == CONNECTION_OK ? YES : NO; } /* message callbacks */ - (BOOL)setNoticeProcessor:(void *)_callback context:(void *)_ctx { #if NG_HAS_NOTICE_PROCESSOR PQsetNoticeProcessor(self->_connection, _callback, _ctx); return YES; // TODO: improve error handling #else return NO; #endif } /* settings */ - (BOOL)setClientEncoding:(NSString *)_encoding { return PQsetClientEncoding(self->_connection, [self _cstrFromString:_encoding]) == 0 ? YES : NO; } /* errors */ - (NSString *)errorMessage { if (![self isValid]) return nil; return [self _stringFromCString:PQerrorMessage(self->_connection)]; } /* queries */ - (void *)rawExecute:(NSString *)_sql { return PQexec(self->_connection, [self _cstrFromString:_sql]); } - (void)clearRawResults:(void *)_ptr { if (_ptr == NULL) return; PQclear(_ptr); } - (PGResultSet *)execute:(NSString *)_sql { void *handle; if ((handle = [self rawExecute:_sql]) == NULL) return nil; return [[[PGResultSet alloc] initWithConnection:self handle:handle] autorelease]; } /* debugging */ - (BOOL)isDebuggingEnabled { return debugOn; } /* description */ - (NSString *)description { NSMutableString *ms; ms = [NSMutableString stringWithCapacity:128]; [ms appendFormat:@"<0x%08X[%@]: ", self, NSStringFromClass([self class])]; if ([self isValid]) [ms appendFormat:@" connection=0x%08X", self->_connection]; else [ms appendString:@" not-connected"]; [ms appendString:@">"]; return ms; } @end /* PGConnection */ @implementation PGResultSet /* wraps PGresult */ - (id)initWithConnection:(PGConnection *)_con handle:(void *)_handle { if (_handle == NULL) { [self release]; return nil; } if ((self = [super init])) { self->connection = [_con retain]; self->results = _handle; } return self; } - (void)dealloc { [self clear]; [self->connection release]; [super dealloc]; } /* accessors */ - (BOOL)isValid { return self->results != NULL ? YES : NO; } - (BOOL)containsBinaryTuples { #if NG_HAS_BINARY_TUPLES if (self->results == NULL) return NO; return PQbinaryTuples(self->results) ? YES : NO; #else return NO; #endif } - (NSString *)commandStatus { char *cstr; if (self->results == NULL) return nil; if ((cstr = PQcmdStatus(self->results)) == NULL) return nil; return [NSString stringWithCString:cstr]; } - (NSString *)commandTuples { char *cstr; if (self->results == NULL) return nil; if ((cstr = PQcmdTuples(self->results)) == NULL) return nil; return [NSString stringWithCString:cstr]; } /* fields */ - (unsigned)fieldCount { return self->results != NULL ? PQnfields(self->results) : 0; } - (NSString *)fieldNameAtIndex:(unsigned int)_idx { // TODO: charset if (self->results == NULL) return nil; return [NSString stringWithCString:PQfname(self->results, _idx)]; } - (int)indexOfFieldNamed:(NSString *)_name { return PQfnumber(self->results, [_name cString]); } - (int)fieldSizeAtIndex:(unsigned int)_idx { if (self->results == NULL) return 0; return PQfsize(self->results, _idx); } - (int)modifierAtIndex:(unsigned int)_idx { if (self->results == NULL) return 0; #if NG_HAS_FMOD return PQfmod(self->results, _idx); #else return 0; #endif } /* tuples */ - (unsigned int)tupleCount { if (self->results == NULL) return 0; return PQntuples(self->results); } - (BOOL)isNullTuple:(int)_tuple atIndex:(unsigned int)_idx { if (self->results == NULL) return NO; return PQgetisnull(self->results, _tuple, _idx) ? YES : NO; } - (void *)rawValueOfTuple:(int)_tuple atIndex:(unsigned int)_idx { if (self->results == NULL) return NULL; return PQgetvalue(self->results, _tuple, _idx); } - (int)lengthOfTuple:(int)_tuple atIndex:(unsigned int)_idx { if (self->results == NULL) return 0; return PQgetlength(self->results, _tuple, _idx); } /* operations */ - (void)clear { if (self->results == NULL) return; PQclear(self->results); self->results = NULL; } @end /* PGResultSet */