aboutsummaryrefslogtreecommitdiffstats
path: root/Frameworks/PostgresKit/Source/PGPostgresConnectionQueryExecution.m
diff options
context:
space:
mode:
Diffstat (limited to 'Frameworks/PostgresKit/Source/PGPostgresConnectionQueryExecution.m')
-rw-r--r--Frameworks/PostgresKit/Source/PGPostgresConnectionQueryExecution.m314
1 files changed, 314 insertions, 0 deletions
diff --git a/Frameworks/PostgresKit/Source/PGPostgresConnectionQueryExecution.m b/Frameworks/PostgresKit/Source/PGPostgresConnectionQueryExecution.m
new file mode 100644
index 00000000..4dec74a6
--- /dev/null
+++ b/Frameworks/PostgresKit/Source/PGPostgresConnectionQueryExecution.m
@@ -0,0 +1,314 @@
+//
+// $Id: PGPostgresConnectionQueryExecution.m 3841 2012-09-10 08:52:00Z stuart02 $
+//
+// PGPostgresConnectionQueryExecution.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 "PGPostgresConnectionQueryExecution.h"
+#import "PGPostgresKitPrivateAPI.h"
+#import "PGPostgresConnectionTypeHandling.h"
+#import "PGPostgresConnectionDelegate.h"
+#import "PGPostgresTypeHandlerProtocol.h"
+#import "PGPostgresConnection.h"
+#import "PGPostgresException.h"
+#import "PGPostgresResult.h"
+#import "PGPostgresStatement.h"
+#import "PGPostgresError.h"
+
+// Constants
+static int PGPostgresResultsAsBinary = 1;
+
+// Internal query structure
+typedef struct
+{
+ int paramNum;
+ const void **paramValues;
+ PGPostgresOid* paramTypes;
+ int *paramLengths;
+ int *paramFormats;
+}
+PGQueryParamData;
+
+@interface PGPostgresConnection ()
+
+- (PGPostgresResult *)_execute:(NSObject *)query values:(NSArray *)values;
+- (BOOL)_queryDidError:(PGresult *)result;
+- (PGQueryParamData *)_createParameterDataStructureWithCount:(int)paramNum;
+- (void)_destroyParamDataStructure:(PGQueryParamData *)paramData;
+
+@end
+
+@implementation PGPostgresConnection (PGPostgresConnectionQueryExecution)
+
+#pragma mark -
+#pragma mark Synchronous Interface
+
+- (PGPostgresResult *)execute:(NSString *)query
+{
+ return [self _execute:query values:nil];
+}
+
+- (PGPostgresResult *)execute:(NSString *)query value:(NSObject *)value
+{
+ return [self _execute:query values:[NSArray arrayWithObject:value]];
+}
+
+- (PGPostgresResult *)executePrepared:(PGPostgresStatement *)statement value:(NSObject *)value
+{
+ return [self _execute:statement values:[NSArray arrayWithObject:value]];
+}
+
+- (PGPostgresResult *)executePrepared:(PGPostgresStatement *)statement
+{
+ return [self _execute:statement values:nil];
+}
+
+- (PGPostgresResult *)executePrepared:(PGPostgresStatement *)statement values:(NSArray *)values
+{
+ return [self _execute:statement values:values];
+}
+
+- (PGPostgresResult *)execute:(NSString *)query values:(NSArray *)values
+{
+ return [self _execute:query values:values];
+}
+
+- (PGPostgresResult *)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);
+
+ PGPostgresResult *result = [self _execute:string values:nil];
+
+ [string release];
+
+ return result;
+}
+
+#pragma mark -
+#pragma mark Asynchronous Interface
+
+#pragma mark -
+#pragma mark Private API
+
+- (PGPostgresResult *)_execute:(NSObject *)query values:(NSArray *)values
+{
+ _lastQueryWasCancelled = NO;
+
+ if (![self isConnected] || !query || ![query isKindOfClass:[NSString class]] || [query isKindOfClass:[PGPostgresStatement class]]) return nil;
+
+ // Notify the delegate
+ if (_delegate && _delegateSupportsWillExecute) {
+ [_delegate connection:self willExecute:query withValues:values];
+ }
+
+ PGQueryParamData *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];
+
+ // 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 <PGPostgresTypeHandlerProtocol> typeHandler = [self typeHandlerForClass:[nativeObject class]];
+
+ if (!typeHandler) {
+ [self _destroyParamDataStructure:paramData];
+
+ // TODO: get rid of exceptions
+ [PGPostgresException raise:PGPostgresConnectionErrorDomain reason:[NSString stringWithFormat:@"Parameter $%u unsupported class %@", (i + 1), NSStringFromClass([nativeObject class])]];
+ return nil;
+ }
+
+ NSData *data = nil; // Sending parameters as binary is not implemented yet
+ PGPostgresOid type = 0;
+
+ if (!data) {
+ [self _destroyParamDataStructure:paramData];
+
+ // TODO: get rid of exceptions
+ [PGPostgresException raise:PGPostgresConnectionErrorDomain 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
+ [PGPostgresException raise:PGPostgresConnectionErrorDomain 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,
+ PGPostgresResultsAsBinary);
+ }
+ else if ([query isKindOfClass:[PGPostgresStatement class]]) {
+ PGPostgresStatement *statement = (PGPostgresStatement *)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,
+ PGPostgresResultsAsBinary);
+ }
+
+ [self _destroyParamDataStructure:paramData];
+
+ if (!result || [self _queryDidError:result]) return nil;
+
+ return [[[PGPostgresResult 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) {
+ if (_lastError) [_lastError release], _lastError = nil;
+
+ _lastError = [[PGPostgresError alloc] initWithResult:result];
+
+ 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.
+ */
+- (PGQueryParamData *)_createParameterDataStructureWithCount:(int)paramNum
+{
+ PGQueryParamData *paramData = malloc(sizeof(PGQueryParamData));
+
+ 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(PGPostgresOid) * 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!
+ [PGPostgresException raise:PGPostgresConnectionErrorDomain 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:(PGQueryParamData *)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