From 3a89bc588f59024618eae0adff79ecd12b7db882 Mon Sep 17 00:00:00 2001 From: Ira Rosen Date: Mon, 5 Jun 2017 11:29:23 +0300 Subject: [PATCH] IBM-Swift/Swift-Kuery#82 Support prepared statements with connection pooling (#19) --- .../PostgreSQLConnection.swift | 29 +++++++++++++++---- .../PostgreSQLPreparedStatement.swift | 2 ++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/Sources/SwiftKueryPostgreSQL/PostgreSQLConnection.swift b/Sources/SwiftKueryPostgreSQL/PostgreSQLConnection.swift index 6ccabbe..a175a89 100644 --- a/Sources/SwiftKueryPostgreSQL/PostgreSQLConnection.swift +++ b/Sources/SwiftKueryPostgreSQL/PostgreSQLConnection.swift @@ -29,6 +29,8 @@ public class PostgreSQLConnection: Connection { private var connectionParameters: String = "" private var inTransaction = false + private var preparedStatements = Set() + /// An indication whether there is a connection to the database. public var isConnected: Bool { return connection != nil @@ -39,7 +41,7 @@ public class PostgreSQLConnection: Connection { init(connectionParameters: String) { self.connectionParameters = connectionParameters - queryBuilder = PostgreSQLConnection.createQuryBuilder() + queryBuilder = PostgreSQLConnection.createQueryBuilder() } /// Initialize an instance of PostgreSQLConnection. @@ -110,7 +112,7 @@ public class PostgreSQLConnection: Connection { } } - private static func createQuryBuilder() -> QueryBuilder { + private static func createQueryBuilder() -> QueryBuilder { let queryBuilder = QueryBuilder(withDeleteRequiresUsing: true, withUpdateRequiresFrom: true, createAutoIncrement: createAutoIncrement) queryBuilder.updateSubstitutions([QueryBuilder.QuerySubstitutionNames.ucase : "UPPER", QueryBuilder.QuerySubstitutionNames.lcase : "LOWER", @@ -277,16 +279,23 @@ public class PostgreSQLConnection: Connection { /// - Throws: QueryError.syntaxError if query build fails, or a database error if it fails to prepare statement. public func prepareStatement(_ raw: String) throws -> PreparedStatement { let statementName = String.randomString() - guard let result = PQprepare(connection, statementName, raw, 0, nil), + if let error = prepareStatement(name: statementName, for: raw) { + throw QueryError.noResult(error) + } + return PostgreSQLPreparedStatement(name: statementName, query: raw) + } + + private func prepareStatement(name: String, for query: String) -> String? { + guard let result = PQprepare(connection, name, query, 0, nil), PQresultStatus(result) == PGRES_COMMAND_OK else { var errorMessage = "Failed to create prepared statement." if let error = String(validatingUTF8: PQerrorMessage(connection)) { errorMessage += " Error: \(error)." } - throw QueryError.noResult(errorMessage) + return errorMessage } - - return PostgreSQLPreparedStatement(name: statementName) + preparedStatements.insert(name) + return nil } /// Execute a prepared statement. @@ -356,6 +365,14 @@ public class PostgreSQLConnection: Connection { onCompletion(.error(QueryError.unsupported("Failed to execute unsupported prepared statement"))) return } + + if !preparedStatements.contains(statement.name) { + if let error = prepareStatement(name: statement.name, for: statement.query) { + onCompletion(.error(QueryError.databaseError(error))) + return + + } + } _ = parameterData.withUnsafeBufferPointer { buffer in PQsendQueryPrepared(connection, statement.name, Int32(parameters.count), buffer.isEmpty ? nil : buffer.baseAddress, nil, nil, 1) diff --git a/Sources/SwiftKueryPostgreSQL/PostgreSQLPreparedStatement.swift b/Sources/SwiftKueryPostgreSQL/PostgreSQLPreparedStatement.swift index db6dd00..639f1a6 100644 --- a/Sources/SwiftKueryPostgreSQL/PostgreSQLPreparedStatement.swift +++ b/Sources/SwiftKueryPostgreSQL/PostgreSQLPreparedStatement.swift @@ -23,4 +23,6 @@ public struct PostgreSQLPreparedStatement: PreparedStatement { /// The name of the prepared statement. public let name: String + + let query: String }