From b84bd42d10076dc2a5d48d4bba518df74f3b4701 Mon Sep 17 00:00:00 2001 From: Timo <38291523+lovetodream@users.noreply.github.com> Date: Wed, 19 Jun 2024 12:11:35 +0200 Subject: [PATCH 1/3] refactor!: rename query to statement/execute --- README.md | 18 +- Snippets/OracleConnection.swift | 2 +- .../Connection/OracleConnection.swift | 59 +++-- .../ConnectionStateMachine.swift | 228 ++++++++-------- ...hine.swift => StatementStateMachine.swift} | 128 ++++----- Sources/OracleNIO/Data/OracleRef.swift | 8 +- .../Documentation.docc/Documentation.md | 4 +- .../Documentation.docc/stored-procedures.md | 4 +- .../Coding/OracleBackendMessageDecoder.swift | 6 +- .../Coding/OracleFrontendMessageEncoder.swift | 78 +++--- .../OracleBackendMessage+BitVector.swift | 2 +- .../OracleBackendMessage+Parameter.swift | 2 +- .../Messages/OracleBackendMessage.swift | 2 +- Sources/OracleNIO/OracleChannelHandler.swift | 77 +++--- Sources/OracleNIO/OracleCodable.swift | 6 +- Sources/OracleNIO/OracleEventsHandler.swift | 8 +- Sources/OracleNIO/OracleRow.swift | 2 +- Sources/OracleNIO/OracleRowStream.swift | 2 +- Sources/OracleNIO/OracleSQLError.swift | 40 +-- ...racleQuery.swift => OracleStatement.swift} | 24 +- Sources/OracleNIO/OracleTask.swift | 46 ++-- Sources/OracleNIO/Pool/OracleClient.swift | 4 +- Tests/IntegrationTests/BugReportTests.swift | 10 +- Tests/IntegrationTests/CustomTypeTests.swift | 20 +- .../IntegrationTests/OracleClientTests.swift | 6 +- Tests/IntegrationTests/OracleNIOTests.swift | 248 +++++++++--------- .../ConnectionStateMachineTests.swift | 6 +- ...swift => StatementStateMachineTests.swift} | 45 ++-- ...Tests.swift => OracleStatementTests.swift} | 24 +- ...ests.swift => StatementContextTests.swift} | 35 +-- .../Utils/ConnectionAction+TestUtils.swift | 20 +- .../ExtendedQueryContext+TestUtils.swift | 8 +- .../Utils/QueryResult+TestUtils.swift | 2 +- 33 files changed, 602 insertions(+), 572 deletions(-) rename Sources/OracleNIO/ConnectionStateMachine/{ExtendedQueryStateMachine.swift => StatementStateMachine.swift} (92%) rename Sources/OracleNIO/{OracleQuery.swift => OracleStatement.swift} (95%) rename Tests/OracleNIOTests/ConnectionStateMachine/{ExtendedQueryStateMachineTests.swift => StatementStateMachineTests.swift} (77%) rename Tests/OracleNIOTests/{OracleQueryTests.swift => OracleStatementTests.swift} (84%) rename Tests/OracleNIOTests/{OracleQueryContextTests.swift => StatementContextTests.swift} (68%) diff --git a/README.md b/README.md index 049e92b..b40e90c 100644 --- a/README.md +++ b/README.md @@ -119,17 +119,17 @@ let connection = try await OracleConnection.connect( try await connection.close() ``` -### Querying +### Running SQL statements -Once a connection is established, queries can be sent to the server. This is very straightforward: +Once a connection is established, statements can be sent to the server. This is very straightforward: ```swift -let rows = try await connection.query("SELECT id, username, birthday FROM users", logger: logger) +let rows = try await connection.execute("SELECT id, username, birthday FROM users", logger: logger) ``` -> `query(_:logger:)` can run either a `Query`, `DML`, `DDL` or even `PlSQL`. +> `execute(_:options:logger:file:line:)` can run either a `Query`, `DML`, `DDL` or even `PlSQL`. -The query will return a `OracleRowSequence`, which is an `AsyncSequence` of `OracleRow`s. The rows can be iterated one-by-one: +The statement will return a `OracleRowSequence`, which is an `AsyncSequence` of `OracleRow`s. The rows can be iterated one-by-one: ```swift for try await row in rows { @@ -165,7 +165,7 @@ A type must implement the `OracleDecodable` protocol in order to be decoded from - `OracleVectorInt8`, `OracleVectorFloat32`, `OracleVectorFloat64` - `RowID` -### Querying with parameters +### Statements with parameters Sending parameterized queries to the database is also supported (in the coolest way possible): @@ -173,16 +173,16 @@ Sending parameterized queries to the database is also supported (in the coolest let id = 1 let username = "fancyuser" let birthday = Date() -try await connection.query(""" +try await connection.execute(""" INSERT INTO users (id, username, birthday) VALUES (\(id), \(username), \(birthday)) """, logger: logger ) ``` -While this looks at first glance like a classic case of [SQL injection](https://en.wikipedia.org/wiki/SQL_injection) 😱, `OracleNIO`'s API ensures that this usage is safe. The first parameter of the `query(_:logger:)` method is not a plain `String`, but a `OracleQuery`, which implements Swift's `ExpressibleByStringInterpolation` protocol. `OracleNIO` uses the literal parts of the provided string as the SQL query and replaces each interpolated value with a parameter binding. Only values which implement the `OracleEncodable` protocol may be interpolated in this way. As with `OracleDecodable`, `OracleNIO` provides default implementations for most common types. +While this looks at first glance like a classic case of [SQL injection](https://en.wikipedia.org/wiki/SQL_injection) 😱, `OracleNIO`'s API ensures that this usage is safe. The first parameter of the `execute(_:options:logger:file:line:)` method is not a plain `String`, but a `OracleStatement`, which implements Swift's `ExpressibleByStringInterpolation` protocol. `OracleNIO` uses the literal parts of the provided string as the SQL statement and replaces each interpolated value with a parameter binding. Only values which implement the `OracleEncodable` protocol may be interpolated in this way. As with `OracleDecodable`, `OracleNIO` provides default implementations for most common types. -Some queries do not receive any rows from the server (most often `INSERT`, `UPDATE`, and `DELETE` queries, not to mention most `DDL` queries). To support this, the `query(_:logger:)` method is marked `@discardableResult`, so that the compiler does not issue a warning if the return value is not used. +Some queries do not receive any rows from the server (most often `INSERT`, `UPDATE`, and `DELETE` queries, not to mention most `DDL` queries). To support this, the `execute(_:options:logger:file:line:)` method is marked `@discardableResult`, so that the compiler does not issue a warning if the return value is not used. ## Changelog diff --git a/Snippets/OracleConnection.swift b/Snippets/OracleConnection.swift index 389496c..27c0168 100644 --- a/Snippets/OracleConnection.swift +++ b/Snippets/OracleConnection.swift @@ -27,7 +27,7 @@ let connection = try await OracleConnection.connect(configuration: configuration // snippet.end // snippet.use -try await connection.query("SELECT 'Hello, World!' FROM dual") +try await connection.execute("SELECT 'Hello, World!' FROM dual") // snippet.end // snippet.close diff --git a/Sources/OracleNIO/Connection/OracleConnection.swift b/Sources/OracleNIO/Connection/OracleConnection.swift index 78d9e88..d1bc043 100644 --- a/Sources/OracleNIO/Connection/OracleConnection.swift +++ b/Sources/OracleNIO/Connection/OracleConnection.swift @@ -40,7 +40,7 @@ import class Foundation.ProcessInfo /// ## Usage /// /// Now you can use the connection to run queries on your database using -/// ``OracleConnection/query(_:options:logger:file:line:)``. +/// ``OracleConnection/execute(_:options:logger:file:line:)``. /// /// @Snippet(path: "oracle-nio/Snippets/OracleConnection", slice: "use") /// @@ -408,22 +408,39 @@ extension OracleConnection { try await self.rollback().get() } - /// Run a query on the Oracle server the connection is connected to. - /// - /// - Parameters: - /// - query: The ``OracleQuery`` to run. - /// - options: A bunch of parameters to optimize the query in different ways. Normally this can - /// be ignored, but feel free to experiment based on your needs. Every option and - /// its impact is documented. - /// - logger: The `Logger` to log query related background events into. Defaults to logging disabled. - /// - file: The file, the query was started in. Used for better error reporting. - /// - line: The line, the query was started in. Used for better error reporting. - /// - Returns: A ``OracleRowSequence`` containing the rows the server sent as the query - /// result. The result sequence can be discarded if the query has no result. + @available(*, deprecated, renamed: "execute(_:options:logger:file:line:)") @discardableResult public func query( _ query: OracleQuery, - options: QueryOptions = .init(), + options: StatementOptions = .init(), + logger: Logger? = nil, + file: String = #fileID, line: Int = #line + ) async throws -> OracleRowSequence { + try await self.execute( + query, + options: options, + logger: logger, + file: file, + line: line + ) + } + + /// Run a statement on the Oracle server the connection is connected to. + /// + /// - Parameters: + /// - statement: The ``OracleStatement`` to run. + /// - options: A bunch of parameters to optimize the statement in different ways. + /// Normally this can be ignored, but feel free to experiment based on your needs. + /// Every option and its impact is documented. + /// - logger: The `Logger` to log statement related background events into. Defaults to logging disabled. + /// - file: The file, the statement was started in. Used for better error reporting. + /// - line: The line, the statement was started in. Used for better error reporting. + /// - Returns: A ``OracleRowSequence`` containing the rows the server sent as the statement + /// result. The result sequence can be discarded if the statement has no result. + @discardableResult + public func execute( + _ statement: OracleStatement, + options: StatementOptions = .init(), logger: Logger? = nil, file: String = #fileID, line: Int = #line ) async throws -> OracleRowSequence { @@ -434,14 +451,14 @@ extension OracleConnection { let promise = self.channel.eventLoop.makePromise( of: OracleRowStream.self ) - let context = ExtendedQueryContext( - query: query, + let context = StatementContext( + statement: statement, options: options, logger: logger, promise: promise ) - self.channel.write(OracleTask.extendedQuery(context), promise: nil) + self.channel.write(OracleTask.statement(context), promise: nil) do { return try await promise.futureResult @@ -450,14 +467,14 @@ extension OracleConnection { } catch var error as OracleSQLError { error.file = file error.line = line - error.query = query + error.statement = statement throw error // rethrow with more metadata } } func execute( cursor: Cursor, - options: QueryOptions = .init(), + options: StatementOptions = .init(), logger: Logger? = nil, file: String = #fileID, line: Int = #line ) async throws -> OracleRowSequence { @@ -468,14 +485,14 @@ extension OracleConnection { let promise = self.channel.eventLoop.makePromise( of: OracleRowStream.self ) - let context = ExtendedQueryContext( + let context = StatementContext( cursor: cursor, options: options, logger: logger, promise: promise ) - self.channel.write(OracleTask.extendedQuery(context), promise: nil) + self.channel.write(OracleTask.statement(context), promise: nil) do { return try await promise.futureResult diff --git a/Sources/OracleNIO/ConnectionStateMachine/ConnectionStateMachine.swift b/Sources/OracleNIO/ConnectionStateMachine/ConnectionStateMachine.swift index a267aab..023ee4e 100644 --- a/Sources/OracleNIO/ConnectionStateMachine/ConnectionStateMachine.swift +++ b/Sources/OracleNIO/ConnectionStateMachine/ConnectionStateMachine.swift @@ -23,8 +23,8 @@ struct ConnectionStateMachine { case dataTypesMessageSent case waitingToStartAuthentication case authenticating(AuthenticationStateMachine) - case readyForQuery - case extendedQuery(ExtendedQueryStateMachine) + case readyForStatement + case statement(StatementStateMachine) case ping(EventLoopPromise) case commit(EventLoopPromise) case rollback(EventLoopPromise) @@ -82,7 +82,7 @@ struct ConnectionStateMachine { case logoffConnection(EventLoopPromise?) case closeConnection(EventLoopPromise?) case fireChannelInactive - case fireEventReadyForQuery + case fireEventReadyForStatement /// Close connection because of an error state. Fail all tasks with the provided error. case closeConnectionAndCleanup(CleanUpContext) @@ -114,19 +114,22 @@ struct ConnectionStateMachine { case failRollback(EventLoopPromise, with: OracleSQLError) case succeedRollback(EventLoopPromise) - // Query - case sendExecute(ExtendedQueryContext, DescribeInfo?) - case sendReexecute(ExtendedQueryContext, CleanupContext) - case sendFetch(ExtendedQueryContext) + // Statement + case sendExecute(StatementContext, DescribeInfo?) + case sendReexecute(StatementContext, CleanupContext) + case sendFetch(StatementContext) case sendFlushOutBinds - case failQuery( + case failStatement( EventLoopPromise, with: OracleSQLError, cleanupContext: CleanUpContext? ) - case succeedQuery(EventLoopPromise, QueryResult) + case succeedStatement( + EventLoopPromise, + StatementResult + ) case needMoreData - // Query streaming + // Statement streaming case forwardRows([DataRow]) case forwardStreamComplete([DataRow], cursorID: UInt16) case forwardStreamError( @@ -188,8 +191,8 @@ struct ConnectionStateMachine { .dataTypesMessageSent, .waitingToStartAuthentication, .authenticating, - .readyForQuery, - .extendedQuery, + .readyForStatement, + .statement, .ping, .commit, .rollback, @@ -217,7 +220,7 @@ struct ConnectionStateMachine { .protocolMessageSent, .waitingToStartAuthentication, .initialized, - .readyForQuery, + .readyForStatement, .ping, .commit, .rollback, @@ -226,11 +229,11 @@ struct ConnectionStateMachine { case .authenticating(var authState): let action = authState.errorHappened(error) return self.modify(with: action) - case .extendedQuery(var queryState): - if queryState.isComplete { + case .statement(var statement): + if statement.isComplete { return self.closeConnectionAndCleanup(error) } else { - let action = queryState.errorHappened(error) + let action = statement.errorHappened(error) return self.modify(with: action) } case .readyToLogOff, .loggingOff, .closing: @@ -265,7 +268,7 @@ struct ConnectionStateMachine { .dataTypesMessageSent, .waitingToStartAuthentication, .authenticating, - .extendedQuery, + .statement, .ping, .commit, .rollback, @@ -273,7 +276,7 @@ struct ConnectionStateMachine { self.taskQueue.append(task) return .wait - case .readyForQuery: + case .readyForStatement: return self.executeTask(task) case .readyToLogOff, .loggingOff, .closing, .closed: @@ -285,15 +288,15 @@ struct ConnectionStateMachine { } switch task { - case .extendedQuery(let queryContext): - switch queryContext.statement { + case .statement(let statementContext): + switch statementContext.type { case .ddl(let promise), .dml(let promise), .plsql(let promise), .query(let promise), .cursor(_, let promise), .plain(let promise): - return .failQuery( + return .failStatement( promise, with: oracleError, cleanupContext: nil ) } @@ -314,7 +317,7 @@ struct ConnectionStateMachine { .dataTypesMessageSent, .waitingToStartAuthentication, .authenticating, - .readyForQuery, + .readyForStatement, .ping, .commit, .rollback, @@ -325,10 +328,10 @@ struct ConnectionStateMachine { .renegotiatingTLS: return .wait - case .extendedQuery(var extendedQuery): + case .statement(var statement): self.state = .modifying // avoid CoW - let action = extendedQuery.channelReadComplete() - self.state = .extendedQuery(extendedQuery) + let action = statement.channelReadComplete() + self.state = .statement(statement) return self.modify(with: action) case .modifying: @@ -343,10 +346,10 @@ struct ConnectionStateMachine { "Received a read event on a connection that was never opened" ) - case .extendedQuery(var extendedQuery): + case .statement(var statement): return self.avoidingStateMachineCoW { machine in - let action = extendedQuery.readEventCaught() - machine.state = .extendedQuery(extendedQuery) + let action = statement.readEventCaught() + machine.state = .statement(statement) return machine.modify(with: action) } @@ -414,9 +417,9 @@ struct ConnectionStateMachine { return .wait case .authenticating: fatalError("Does this even happen?") - case .readyForQuery: + case .readyForStatement: return .wait - case .extendedQuery: + case .statement: fatalError("Does this even happen?") case .ping: return .sendPing @@ -473,7 +476,7 @@ struct ConnectionStateMachine { return machine.modify(with: action) } - case .readyForQuery, .extendedQuery, .ping, .commit, .rollback: + case .readyForStatement, .statement, .ping, .commit, .rollback: fatalError("Is this possible?") case .readyToLogOff, .loggingOff, .closing, .closed, .modifying: @@ -485,7 +488,7 @@ struct ConnectionStateMachine { switch self.state { case .initialized, .waitingToStartAuthentication, - .readyForQuery, + .readyForStatement, .readyToLogOff, .closed, .renegotiatingTLS: @@ -494,7 +497,7 @@ struct ConnectionStateMachine { .protocolMessageSent, .dataTypesMessageSent, .authenticating, - .extendedQuery, + .statement, .ping, .commit, .rollback: @@ -528,8 +531,8 @@ struct ConnectionStateMachine { .dataTypesMessageSent, .waitingToStartAuthentication, .authenticating, - .readyForQuery, - .extendedQuery, + .readyForStatement, + .statement, .renegotiatingTLS: return self.errorHappened( .unexpectedBackendMessage(.status(status)) @@ -565,10 +568,10 @@ struct ConnectionStateMachine { _ describeInfo: DescribeInfo ) -> ConnectionAction { switch self.state { - case .extendedQuery(var queryState): + case .statement(var statement): return self.avoidingStateMachineCoW { machine in - let action = queryState.describeInfoReceived(describeInfo) - machine.state = .extendedQuery(queryState) + let action = statement.describeInfoReceived(describeInfo) + machine.state = .statement(statement) return machine.modify(with: action) } default: @@ -582,10 +585,10 @@ struct ConnectionStateMachine { _ rowHeader: OracleBackendMessage.RowHeader ) -> ConnectionAction { switch self.state { - case .extendedQuery(var queryState): + case .statement(var statement): return self.avoidingStateMachineCoW { machine in - let action = queryState.rowHeaderReceived(rowHeader) - machine.state = .extendedQuery(queryState) + let action = statement.rowHeaderReceived(rowHeader) + machine.state = .statement(statement) return machine.modify(with: action) } default: @@ -600,12 +603,12 @@ struct ConnectionStateMachine { capabilities: Capabilities ) -> ConnectionAction { switch self.state { - case .extendedQuery(var queryState): + case .statement(var statement): return self.avoidingStateMachineCoW { machine in - let action = queryState.rowDataReceived( + let action = statement.rowDataReceived( rowData, capabilities: capabilities ) - machine.state = .extendedQuery(queryState) + machine.state = .statement(statement) return machine.modify(with: action) } default: @@ -625,10 +628,10 @@ struct ConnectionStateMachine { _ bitVector: OracleBackendMessage.BitVector ) -> ConnectionAction { switch self.state { - case .extendedQuery(var queryState): + case .statement(var statement): return self.avoidingStateMachineCoW { machine in - let action = queryState.bitVectorReceived(bitVector) - machine.state = .extendedQuery(queryState) + let action = statement.bitVectorReceived(bitVector) + machine.state = .statement(statement) return machine.modify(with: action) } default: @@ -640,10 +643,10 @@ struct ConnectionStateMachine { _ error: OracleBackendMessage.BackendError ) -> ConnectionAction { switch self.state { - case .extendedQuery(var queryState): + case .statement(var statement): return self.avoidingStateMachineCoW { machine in - let action = queryState.errorReceived(error) - machine.state = .extendedQuery(queryState) + let action = statement.errorReceived(error) + machine.state = .statement(statement) return machine.modify(with: action) } case .authenticating(var authState): @@ -659,62 +662,62 @@ struct ConnectionStateMachine { } } - mutating func cancelQueryStream() -> ConnectionAction { - guard case .extendedQuery(var queryState) = state else { - preconditionFailure("Tried to cancel stream without active query") + mutating func cancelStatementStream() -> ConnectionAction { + guard case .statement(var statement) = state else { + preconditionFailure("Tried to cancel stream without active statement") } return self.avoidingStateMachineCoW { machine in - let action = queryState.cancel() - machine.state = .extendedQuery(queryState) + let action = statement.cancel() + machine.state = .statement(statement) return machine.modify(with: action) } } - mutating func queryStreamCancelled() -> ConnectionAction { - guard case .extendedQuery = state else { - preconditionFailure("Tried to cancel stream without active query") + mutating func statementStreamCancelled() -> ConnectionAction { + guard case .statement = state else { + preconditionFailure("Tried to cancel stream without active statement") } self.markerState = .markerSent return .sendMarker } - mutating func requestQueryRows() -> ConnectionAction { - guard case .extendedQuery(var queryState) = state else { + mutating func requestStatementRows() -> ConnectionAction { + guard case .statement(var statement) = state else { preconditionFailure( - "Tried to consume next row, without active query" + "Tried to consume next row, without active statement" ) } return self.avoidingStateMachineCoW { machine in - let action = queryState.requestQueryRows() - machine.state = .extendedQuery(queryState) + let action = statement.requestStatementRows() + machine.state = .statement(statement) return machine.modify(with: action) } } - mutating func readyForQueryReceived() -> ConnectionAction { + mutating func readyForStatementReceived() -> ConnectionAction { switch self.state { - case .extendedQuery(let extendedQuery): - guard extendedQuery.isComplete else { + case .statement(let statement): + guard statement.isComplete else { preconditionFailure( """ - Ready for query received when query is still being executed + readyForStatement received when statement is still being executed """) } - self.state = .readyForQuery - return self.executeNextQueryFromQueue() + self.state = .readyForStatement + return self.executeNextStatementFromQueue() case .ping, .commit, .rollback: - self.state = .readyForQuery - return self.executeNextQueryFromQueue() + self.state = .readyForStatement + return self.executeNextStatementFromQueue() case .loggingOff, .closing: // Might happen if the connection is getting closed immediately // after a ping. In that case the ping's success or failure response - // triggers a readyForQueryReceived, while we are already closing. + // triggers a readyForStatementReceived, while we are already closing. // (This race might not be exclusive to ping's) return .wait @@ -727,12 +730,12 @@ struct ConnectionStateMachine { _ buffer: ByteBuffer, capabilities: Capabilities ) -> ConnectionAction { switch self.state { - case .extendedQuery(var queryState): + case .statement(var statement): return self.avoidingStateMachineCoW { machine in - let action = queryState.chunkReceived( + let action = statement.chunkReceived( buffer, capabilities: capabilities ) - machine.state = .extendedQuery(queryState) + machine.state = .statement(statement) return machine.modify(with: action) } @@ -749,25 +752,25 @@ struct ConnectionStateMachine { mutating func ioVectorReceived( _ vector: OracleBackendMessage.InOutVector ) -> ConnectionAction { - guard case .extendedQuery(var queryState) = self.state else { + guard case .statement(var statement) = self.state else { preconditionFailure("Invalid state: \(self.state)") } return self.avoidingStateMachineCoW { machine in - let action = queryState.ioVectorReceived(vector) - machine.state = .extendedQuery(queryState) + let action = statement.ioVectorReceived(vector) + machine.state = .statement(statement) return machine.modify(with: action) } } mutating func flushOutBindsReceived() -> ConnectionAction { - guard case .extendedQuery(var queryState) = self.state else { + guard case .statement(var statement) = self.state else { preconditionFailure("Invalid state: \(self.state)") } return self.avoidingStateMachineCoW { machine in - let action = queryState.flushOutBindsReceived() - machine.state = .extendedQuery(queryState) + let action = statement.flushOutBindsReceived() + machine.state = .statement(statement) return machine.modify(with: action) } } @@ -815,7 +818,7 @@ struct ConnectionStateMachine { .protocolMessageSent, .dataTypesMessageSent, .waitingToStartAuthentication, - .readyForQuery, + .readyForStatement, .renegotiatingTLS: let cleanupContext = self.setErrorAndCreateCleanupContext( error, closePromise: closePromise @@ -848,24 +851,25 @@ struct ConnectionStateMachine { } return .closeConnectionAndCleanup(cleanupContext) - case .extendedQuery(var queryState): + case .statement(var statement): let cleanupContext = self.setErrorAndCreateCleanupContext( error, closePromise: closePromise ) - if queryState.isComplete { - // in case the query state machine is complete, all necessary - // actions have already been forwarded to the consumer. We can - // close and cleanup without caring about the substate machine. + if statement.isComplete { + // in case the statement state machine is complete, all + // necessary actions have already been forwarded to the + // consumer. We can close and cleanup without caring about + // the substate machine. return .closeConnectionAndCleanup(cleanupContext) } - switch queryState.errorHappened(error) { + switch statement.errorHappened(error) { case .sendExecute, .sendReexecute, .sendFetch, .sendFlushOutBinds, - .succeedQuery, + .succeedStatement, .needMoreData, .forwardRows, .forwardStreamComplete, @@ -877,8 +881,8 @@ struct ConnectionStateMachine { case .evaluateErrorAtConnectionLevel: return .closeConnectionAndCleanup(cleanupContext) - case .failQuery(let promise, with: let error): - return .failQuery( + case .failStatement(let promise, with: let error): + return .failStatement( promise, with: error, cleanupContext: cleanupContext ) @@ -911,10 +915,10 @@ struct ConnectionStateMachine { } } - private mutating func executeNextQueryFromQueue() -> ConnectionAction { - guard case .readyForQuery = state else { + private mutating func executeNextStatementFromQueue() -> ConnectionAction { + guard case .readyForStatement = state else { preconditionFailure( - "Only expected to be invoked, if we are readyForQuery" + "Only expected to be invoked, if we are readyForStatement" ) } @@ -929,24 +933,24 @@ struct ConnectionStateMachine { return .closeConnection(closePromise) } - return .fireEventReadyForQuery + return .fireEventReadyForStatement } private mutating func executeTask(_ task: OracleTask) -> ConnectionAction { - guard case .readyForQuery = state else { + guard case .readyForStatement = state else { preconditionFailure( - "Only expected to be invoked, if we are readyToQuery" + "Only expected to be invoked, if we are readyForStatement" ) } switch task { - case .extendedQuery(let queryContext): + case .statement(let statementContext): return self.avoidingStateMachineCoW { machine in - var extendedQuery = ExtendedQueryStateMachine( - queryContext: queryContext + var statement = StatementStateMachine( + statementContext: statementContext ) - let action = extendedQuery.start() - machine.state = .extendedQuery(extendedQuery) + let action = statement.start() + machine.state = .statement(statement) return machine.modify(with: action) } case .ping(let promise): @@ -982,7 +986,7 @@ extension ConnectionStateMachine { .sidNotSupported, .uncleanShutdown: return true - case .queryCancelled, .nationalCharsetNotSupported: + case .statementCancelled, .nationalCharsetNotSupported: return false case .server: switch error.serverInfo?.number { @@ -1085,7 +1089,7 @@ extension ConnectionStateMachine { case .wait: return .wait case .authenticated(let parameters): - self.state = .readyForQuery + self.state = .readyForStatement return .authenticated(parameters) case .reportAuthenticationError(let error): let cleanupContext = self.setErrorAndCreateCleanupContext(error) @@ -1096,21 +1100,21 @@ extension ConnectionStateMachine { extension ConnectionStateMachine { mutating func modify( - with action: ExtendedQueryStateMachine.Action + with action: StatementStateMachine.Action ) -> ConnectionAction { switch action { case .sendExecute(let context, let describeInfo): return .sendExecute(context, describeInfo) - case .sendReexecute(let queryContext, let cleanupContext): - return .sendReexecute(queryContext, cleanupContext) + case .sendReexecute(let statementContxt, let cleanupContext): + return .sendReexecute(statementContxt, cleanupContext) case .sendFetch(let context): return .sendFetch(context) case .sendFlushOutBinds: return .sendFlushOutBinds - case .failQuery(let promise, let error): - return .failQuery(promise, with: error, cleanupContext: nil) - case .succeedQuery(let promise, let columns): - return .succeedQuery(promise, columns) + case .failStatement(let promise, let error): + return .failStatement(promise, with: error, cleanupContext: nil) + case .succeedStatement(let promise, let columns): + return .succeedStatement(promise, columns) case .needMoreData: return .needMoreData case .forwardRows(let rows): @@ -1127,7 +1131,7 @@ extension ConnectionStateMachine { clientCancelled: clientCancelled ) case .forwardCancelComplete: - return self.readyForQueryReceived() + return self.readyForStatementReceived() case .evaluateErrorAtConnectionLevel(let error): if let cleanupContext = self.setErrorAndCreateCleanupContextIfNeeded(error) diff --git a/Sources/OracleNIO/ConnectionStateMachine/ExtendedQueryStateMachine.swift b/Sources/OracleNIO/ConnectionStateMachine/StatementStateMachine.swift similarity index 92% rename from Sources/OracleNIO/ConnectionStateMachine/ExtendedQueryStateMachine.swift rename to Sources/OracleNIO/ConnectionStateMachine/StatementStateMachine.swift index 9c87048..3264a4c 100644 --- a/Sources/OracleNIO/ConnectionStateMachine/ExtendedQueryStateMachine.swift +++ b/Sources/OracleNIO/ConnectionStateMachine/StatementStateMachine.swift @@ -13,26 +13,26 @@ import NIOCore -struct ExtendedQueryStateMachine { +struct StatementStateMachine { private enum State { - case initialized(ExtendedQueryContext) - case describeInfoReceived(ExtendedQueryContext, DescribeInfo) + case initialized(StatementContext) + case describeInfoReceived(StatementContext, DescribeInfo) case streaming( - ExtendedQueryContext, + StatementContext, DescribeInfo, OracleBackendMessage.RowHeader, RowStreamStateMachine ) case streamingAndWaiting( - ExtendedQueryContext, + StatementContext, DescribeInfo, OracleBackendMessage.RowHeader, RowStreamStateMachine, partial: ByteBuffer ) - /// Indicates that the current query was cancelled and we want to drain rows from the - /// connection ASAP. + /// Indicates that the current statement was cancelled and we want to drain + /// rows from the connection ASAP. case drain([OracleColumn]) case commandComplete @@ -42,13 +42,13 @@ struct ExtendedQueryStateMachine { } enum Action { - case sendExecute(ExtendedQueryContext, DescribeInfo?) - case sendReexecute(ExtendedQueryContext, CleanupContext) - case sendFetch(ExtendedQueryContext) + case sendExecute(StatementContext, DescribeInfo?) + case sendReexecute(StatementContext, CleanupContext) + case sendFetch(StatementContext) case sendFlushOutBinds - case failQuery(EventLoopPromise, with: OracleSQLError) - case succeedQuery(EventLoopPromise, QueryResult) + case failStatement(EventLoopPromise, with: OracleSQLError) + case succeedStatement(EventLoopPromise, StatementResult) case evaluateErrorAtConnectionLevel(OracleSQLError) @@ -77,30 +77,30 @@ struct ExtendedQueryStateMachine { private var state: State private var isCancelled: Bool - init(queryContext: ExtendedQueryContext) { + init(statementContext: StatementContext) { self.isCancelled = false - self.state = .initialized(queryContext) + self.state = .initialized(statementContext) } mutating func start() -> Action { - guard case .initialized(let queryContext) = state else { + guard case .initialized(let statementContext) = state else { preconditionFailure( - "Start should only be called, if the query has been initialized" + "Start should only be called, if the statement has been initialized" ) } - if case .cursor(let cursor, _) = queryContext.statement { - self.state = .describeInfoReceived(queryContext, cursor.describeInfo) + if case .cursor(let cursor, _) = statementContext.type { + self.state = .describeInfoReceived(statementContext, cursor.describeInfo) } - return .sendExecute(queryContext, nil) + return .sendExecute(statementContext, nil) } mutating func cancel() -> Action { switch self.state { case .initialized: preconditionFailure( - "Start must be called immediately after the query was created" + "Start must be called immediately after the statement was created" ) case .describeInfoReceived(let context, _): @@ -109,14 +109,14 @@ struct ExtendedQueryStateMachine { } self.isCancelled = true - switch context.statement { + switch context.type { case .ddl(let promise), .dml(let promise), .plsql(let promise), .query(let promise), .cursor(_, let promise), .plain(let promise): - return .failQuery(promise, with: .queryCancelled) + return .failStatement(promise, with: .statementCancelled) } case .streaming(_, let describeInfo, _, var streamStateMachine), @@ -129,11 +129,11 @@ struct ExtendedQueryStateMachine { switch streamStateMachine.fail() { case .wait: return .forwardStreamError( - .queryCancelled, read: false, clientCancelled: true + .statementCancelled, read: false, clientCancelled: true ) case .read: return .forwardStreamError( - .queryCancelled, read: true, clientCancelled: true + .statementCancelled, read: true, clientCancelled: true ) } @@ -167,16 +167,16 @@ struct ExtendedQueryStateMachine { state = .streaming(context, describeInfo, rowHeader, .init()) } - switch context.statement { + switch context.type { case .ddl(let promise), .dml(let promise), .plsql(let promise), .query(let promise), .cursor(_, let promise), .plain(let promise): - return .succeedQuery( + return .succeedStatement( promise, - QueryResult( + StatementResult( value: .describeInfo(describeInfo.columns), logger: context.logger ) @@ -196,7 +196,7 @@ struct ExtendedQueryStateMachine { return .wait case .drain: - // This state might occur, if the client cancelled the query, + // This state might occur, if the client cancelled the statement, // but the server did not yet receive/process the cancellation // marker. Due to that it might send more data without knowing yet. return .wait @@ -215,7 +215,7 @@ struct ExtendedQueryStateMachine { ) -> Action { switch self.state { case .initialized(let context): - let outBinds = context.query.binds.metadata.compactMap(\.outContainer) + let outBinds = context.statement.binds.metadata.compactMap(\.outContainer) guard !outBinds.isEmpty else { preconditionFailure() } var buffer = rowData.slice if context.isReturning { @@ -293,7 +293,7 @@ struct ExtendedQueryStateMachine { } case .drain: - // This state might occur, if the client cancelled the query, + // This state might occur, if the client cancelled the statement, // but the server did not yet receive/process the cancellation // marker. Due to that it might send more data without knowing yet. return .wait @@ -342,14 +342,14 @@ struct ExtendedQueryStateMachine { state = .commandComplete } - switch context.statement { + switch context.type { case .query(let promise), .plsql(let promise), .dml(let promise), .ddl(let promise), .cursor(_, let promise), .plain(let promise): - action = .succeedQuery( + action = .succeedStatement( promise, .init(value: .noRows, logger: context.logger) ) // empty response } @@ -378,14 +378,14 @@ struct ExtendedQueryStateMachine { case .initialized(let context): context.cursorID = cursor - switch context.statement { + switch context.type { case .query(let promise), .plsql(let promise), .dml(let promise), .ddl(let promise), .cursor(_, let promise), .plain(let promise): - action = .failQuery(promise, with: .server(error)) + action = .failStatement(promise, with: .server(error)) } default: action = .forwardStreamError( @@ -404,14 +404,14 @@ struct ExtendedQueryStateMachine { case .initialized(let context): context.cursorID = cursor - switch context.statement { + switch context.type { case .query(let promise), .plsql(let promise), .dml(let promise), .ddl(let promise), .cursor(_, let promise), .plain(let promise): - action = .failQuery(promise, with: .server(error)) + action = .failStatement(promise, with: .server(error)) } default: if exception != .integrityError { @@ -439,16 +439,16 @@ struct ExtendedQueryStateMachine { if let cursorID = error.cursorID { context.cursorID = cursorID } - switch context.statement { + switch context.type { case .query(let promise), .plsql(let promise), .dml(let promise), .ddl(let promise), .cursor(_, let promise), .plain(let promise): - action = .succeedQuery( + action = .succeedStatement( promise, - QueryResult( + StatementResult( value: .noRows, logger: context.logger ) ) @@ -501,14 +501,14 @@ struct ExtendedQueryStateMachine { } } else if error.number != 0 { - switch context.statement { + switch context.type { case .query(let promise), .plsql(let promise), .dml(let promise), .ddl(let promise), .cursor(_, let promise), .plain(let promise): - action = .failQuery(promise, with: .server(error)) + action = .failStatement(promise, with: .server(error)) } self.avoidingStateMachineCoWVoid { state in @@ -518,13 +518,13 @@ struct ExtendedQueryStateMachine { action = .sendFetch(context) } - case .streaming(let extendedQueryContext, _, _, _), - .streamingAndWaiting(let extendedQueryContext, _, _, _, _): + case .streaming(let statementContext, _, _, _), + .streamingAndWaiting(let statementContext, _, _, _, _): // no error actually happened, we need more rows if let cursorID = error.cursorID { - extendedQueryContext.cursorID = cursorID + statementContext.cursorID = cursorID } - action = .sendFetch(extendedQueryContext) + action = .sendFetch(statementContext) case .modifying: preconditionFailure("Invalid state: \(self.state)") @@ -543,12 +543,12 @@ struct ExtendedQueryStateMachine { ) -> Action { switch self.state { case .drain: - // Could happen if we cancelled the query while the database is + // Could happen if we cancelled the statement while the database is // still sending stuff return .wait case .streamingAndWaiting( - let extendedQueryContext, + let statementContext, let describeInfo, let rowHeader, let streamState, @@ -557,13 +557,13 @@ struct ExtendedQueryStateMachine { partial.writeImmutableBuffer(buffer) self.avoidingStateMachineCoWVoid { state in state = .streaming( - extendedQueryContext, describeInfo, rowHeader, streamState + statementContext, describeInfo, rowHeader, streamState ) } return self.moreDataReceived( &partial, capabilities: capabilities, - context: extendedQueryContext, + context: statementContext, describeInfo: describeInfo ) @@ -577,10 +577,10 @@ struct ExtendedQueryStateMachine { ) -> Action { switch self.state { case .initialized(let context): - guard context.query.binds.count == vector.bindMetadata.count else { + guard context.statement.binds.count == vector.bindMetadata.count else { preconditionFailure( """ - mismatch in binds - sent: \(context.query.binds.count), \ + mismatch in binds - sent: \(context.statement.binds.count), \ received: \(vector.bindMetadata.count) """) } @@ -640,17 +640,17 @@ struct ExtendedQueryStateMachine { .commandComplete, .error: preconditionFailure( - "We can't send a fetch if the query completed already" + "We can't send a fetch if the statement completed already" ) case .modifying: preconditionFailure("Invalid state: \(self.state)") } } - mutating func requestQueryRows() -> Action { + mutating func requestStatementRows() -> Action { switch self.state { case .streaming( - let queryContext, + let statementContext, let describeInfo, let rowHeader, var demandStateMachine @@ -658,7 +658,7 @@ struct ExtendedQueryStateMachine { return self.avoidingStateMachineCoW { state in let action = demandStateMachine.demandMoreResponseBodyParts() state = .streaming( - queryContext, describeInfo, rowHeader, demandStateMachine + statementContext, describeInfo, rowHeader, demandStateMachine ) switch action { case .read: @@ -779,7 +779,7 @@ struct ExtendedQueryStateMachine { .error, .describeInfoReceived: // we already have the complete stream received, now we are waiting - // for a `readyForQuery` package. To receive this we need to read. + // for a `readyForStatement` package. To receive this we need to read. return .read case .modifying: @@ -792,7 +792,7 @@ struct ExtendedQueryStateMachine { private mutating func moreDataReceived( _ buffer: inout ByteBuffer, capabilities: Capabilities, - context: ExtendedQueryContext, + context: StatementContext, describeInfo: DescribeInfo? ) -> Action { while buffer.readableBytes > 0 { @@ -810,7 +810,7 @@ struct ExtendedQueryStateMachine { do { let decodingContext = OracleBackendMessageDecoder.Context( capabilities: capabilities) - decodingContext.queryOptions = context.options + decodingContext.statementOptions = context.options decodingContext.columnsCount = describeInfo?.columns.count var messages: TinySequence = [] try OracleBackendMessage.decodeData( @@ -938,7 +938,7 @@ struct ExtendedQueryStateMachine { if self.isCancelled { return .evaluateErrorAtConnectionLevel(error) } else { - switch context.statement { + switch context.type { case .ddl(let promise), .dml(let promise), .plsql(let promise), @@ -946,7 +946,7 @@ struct ExtendedQueryStateMachine { .cursor(_, let promise), .plain(let promise): self.state = .error(error) - return .failQuery(promise, with: error) + return .failStatement(promise, with: error) } } @@ -967,9 +967,9 @@ struct ExtendedQueryStateMachine { case .commandComplete, .error: preconditionFailure( """ - This state must not be reached. If the query `.isComplete`, the - ConnectionStateMachine must not send any further events to the - substate machine. + This state must not be reached. If the statement `.isComplete`, + the ConnectionStateMachine must not send any further events to + the substate machine. """) case .modifying: @@ -1206,7 +1206,7 @@ struct ExtendedQueryStateMachine { } } -extension ExtendedQueryStateMachine { +extension StatementStateMachine { /// While the state machine logic above is great, there is a downside to having all of the state machine /// data in associated data on enumerations: any modification of that data will trigger copy on write /// for heap-allocated data. That means that for _every operation on the state machine_ we will CoW diff --git a/Sources/OracleNIO/Data/OracleRef.swift b/Sources/OracleNIO/Data/OracleRef.swift index 52c72bd..dc62b31 100644 --- a/Sources/OracleNIO/Data/OracleRef.swift +++ b/Sources/OracleNIO/Data/OracleRef.swift @@ -24,7 +24,7 @@ import NIOCore /// /// ```swift /// let ref = OracleRef(dataType: .number, isReturnBind: true) -/// try await connection.query( +/// try await connection.execute( /// "INSERT INTO table(id) VALUES (1) RETURNING id INTO \(ref)", /// logger: logger /// ) @@ -35,7 +35,7 @@ import NIOCore /// /// ```swift /// let ref = OracleRef(dataType: .number) -/// try await conn.query(""" +/// try await conn.execute(""" /// begin /// \(ref) := \(OracleNumber(8)) + \(OracleNumber(7)); /// end; @@ -47,7 +47,7 @@ import NIOCore /// /// ```swift /// let ref = OracleRef(OracleNumber(25)) -/// try await conn.query(""" +/// try await conn.execute(""" /// begin /// \(ref) := \(ref) + \(OracleNumber(8)) + \(OracleNumber(7)); /// end; @@ -76,7 +76,7 @@ public final class OracleRef: Sendable, Hashable { /// Use this initializer to create an OUT bind. /// /// Please be aware that you still have to decode the database response into the Swift type you want - /// after completing the query (using ``decode(of:)``). + /// after completing the statement (using ``decode(of:)``). /// /// - Parameter dataType: The desired datatype within the Oracle database. /// - Parameter isReturnBind: Set this to `true` if the bind is used as part of a DML diff --git a/Sources/OracleNIO/Documentation.docc/Documentation.md b/Sources/OracleNIO/Documentation.docc/Documentation.md index cc16a97..3775764 100644 --- a/Sources/OracleNIO/Documentation.docc/Documentation.md +++ b/Sources/OracleNIO/Documentation.docc/Documentation.md @@ -39,14 +39,14 @@ Oracle Database 12.1 or later. ### Querying -- ``OracleQuery`` +- ``OracleStatement`` - ``OracleBindings`` - ``OracleRow`` - ``OracleRowSequence`` - ``OracleRandomAccessRow`` - ``OracleCell`` - ``OracleColumn`` -- ``QueryOptions`` +- ``StatementOptions`` ### Encoding and Decoding diff --git a/Sources/OracleNIO/Documentation.docc/stored-procedures.md b/Sources/OracleNIO/Documentation.docc/stored-procedures.md index 39dc635..4780bb3 100644 --- a/Sources/OracleNIO/Documentation.docc/stored-procedures.md +++ b/Sources/OracleNIO/Documentation.docc/stored-procedures.md @@ -1,6 +1,6 @@ # PL/SQL Stored Procedures -You can call PL/SQL stored procedures, functions and anonymous blocks from OracleNIO using ``OracleConnection/query(_:options:logger:file:line:)``. +You can call PL/SQL stored procedures, functions and anonymous blocks from OracleNIO using ``OracleConnection/execute(_:options:logger:file:line:)``. ## Overview @@ -20,7 +20,7 @@ You can use the following Swift code to call the procedure. ```swift let outBind = OracleRef(OracleNumber(0)) -try await connection.query(""" +try await connection.execute(""" begin myproc(\(OracleNumber(123)), \(outBind)); end; diff --git a/Sources/OracleNIO/Messages/Coding/OracleBackendMessageDecoder.swift b/Sources/OracleNIO/Messages/Coding/OracleBackendMessageDecoder.swift index 3ef78b0..2cb3976 100644 --- a/Sources/OracleNIO/Messages/Coding/OracleBackendMessageDecoder.swift +++ b/Sources/OracleNIO/Messages/Coding/OracleBackendMessageDecoder.swift @@ -35,18 +35,18 @@ struct OracleBackendMessageDecoder: ByteToMessageDecoder { final class Context { var capabilities: Capabilities var performingChunkedRead = false - var queryOptions: QueryOptions? = nil + var statementOptions: StatementOptions? = nil var columnsCount: Int? = nil init( capabilities: Capabilities, performingChunkedRead: Bool = false, - queryOptions: QueryOptions? = nil, + statementOptions: StatementOptions? = nil, columnsCount: Int? = nil ) { self.capabilities = capabilities self.performingChunkedRead = performingChunkedRead - self.queryOptions = queryOptions + self.statementOptions = statementOptions self.columnsCount = columnsCount } } diff --git a/Sources/OracleNIO/Messages/Coding/OracleFrontendMessageEncoder.swift b/Sources/OracleNIO/Messages/Coding/OracleFrontendMessageEncoder.swift index 6b730dd..dca0dab 100644 --- a/Sources/OracleNIO/Messages/Coding/OracleFrontendMessageEncoder.swift +++ b/Sources/OracleNIO/Messages/Coding/OracleFrontendMessageEncoder.swift @@ -434,14 +434,14 @@ struct OracleFrontendMessageEncoder { } mutating func execute( - queryContext: ExtendedQueryContext, + statementContext: StatementContext, cleanupContext: CleanupContext, describeInfo: DescribeInfo? ) { self.clearIfNeeded() - let query = queryContext.query - let queryOptions = queryContext.options + let statement = statementContext.statement + let statementOptions = statementContext.options // 1. options var options: UInt32 = 0 @@ -449,43 +449,43 @@ struct OracleFrontendMessageEncoder { var parametersCount: UInt32 = 0 var iterationsCount: UInt32 = 1 - if !queryContext.requiresDefine && query.binds.count != 0 { - parametersCount = .init(query.binds.count) + if !statementContext.requiresDefine && statement.binds.count != 0 { + parametersCount = .init(statement.binds.count) } - if queryContext.requiresDefine { + if statementContext.requiresDefine { options |= Constants.TNS_EXEC_OPTION_DEFINE - } else if !query.sql.isEmpty { + } else if !statement.sql.isEmpty { dmlOptions = Constants.TNS_EXEC_OPTION_IMPLICIT_RESULTSET options |= Constants.TNS_EXEC_OPTION_EXECUTE } - if queryContext.cursorID == 0 || queryContext.statement.isDDL { + if statementContext.cursorID == 0 || statementContext.type.isDDL { options |= Constants.TNS_EXEC_OPTION_PARSE } - if queryContext.statement.isQuery { - if queryContext.cursorID == 0 || queryContext.requiresDefine { - iterationsCount = UInt32(queryOptions.prefetchRows) + if statementContext.type.isQuery { + if statementContext.cursorID == 0 || statementContext.requiresDefine { + iterationsCount = UInt32(statementOptions.prefetchRows) } else { - iterationsCount = UInt32(queryOptions.arraySize) + iterationsCount = UInt32(statementOptions.arraySize) } - if iterationsCount > 0 && !queryContext.noPrefetch { + if iterationsCount > 0 && !statementContext.noPrefetch { options |= Constants.TNS_EXEC_OPTION_FETCH } } - if !queryContext.statement.isPlSQL { + if !statementContext.type.isPlSQL { options |= Constants.TNS_EXEC_OPTION_NOT_PLSQL - } else if queryContext.statement.isPlSQL && parametersCount > 0 { + } else if statementContext.type.isPlSQL && parametersCount > 0 { options |= Constants.TNS_EXEC_OPTION_PLSQL_BIND } if parametersCount > 0 { options |= Constants.TNS_EXEC_OPTION_BIND } - if queryContext.options.batchErrors { + if statementContext.options.batchErrors { options |= Constants.TNS_EXEC_OPTION_BATCH_ERRORS } - if queryContext.options.arrayDMLRowCounts { + if statementContext.options.arrayDMLRowCounts { options |= Constants.TNS_EXEC_OPTION_DML_ROWCOUNTS } - if queryOptions.autoCommit { + if statementOptions.autoCommit { options |= Constants.TNS_EXEC_OPTION_COMMIT } @@ -498,15 +498,15 @@ struct OracleFrontendMessageEncoder { self.writeFunctionCode( messageType: .function, functionCode: .execute, - sequenceNumber: &queryContext.sequenceNumber + sequenceNumber: &statementContext.sequenceNumber ) // 4. write body of message self.buffer.writeUB4(options) // execute options - self.buffer.writeUB4(UInt32(queryContext.cursorID)) // cursor ID - if queryContext.cursorID == 0 || queryContext.statement.isDDL { + self.buffer.writeUB4(UInt32(statementContext.cursorID)) // cursor ID + if statementContext.cursorID == 0 || statementContext.type.isDDL { self.buffer.writeInteger(UInt8(1)) // pointer (cursor ID) - self.buffer.writeUB4(queryContext.sqlLength) + self.buffer.writeUB4(statementContext.sqlLength) } else { self.buffer.writeInteger(UInt8(0)) // pointer (cursor ID) self.buffer.writeUB4(0) @@ -530,7 +530,7 @@ struct OracleFrontendMessageEncoder { self.buffer.writeInteger(UInt8(0)) // pointer (al8txl) self.buffer.writeInteger(UInt8(0)) // pointer (al8kv) self.buffer.writeInteger(UInt8(0)) // pointer (al8kvl) - if queryContext.requiresDefine { + if statementContext.requiresDefine { self.buffer.writeInteger(UInt8(1)) // pointer (al8doac) self.buffer.writeUB4( UInt32(describeInfo?.columns.count ?? 0) @@ -547,7 +547,7 @@ struct OracleFrontendMessageEncoder { self.buffer.writeInteger(UInt8(0)) // pointer (al8dnam) self.buffer.writeUB4(0) // al8dnaml self.buffer.writeUB4(0) // al8regid_msb - if queryOptions.arrayDMLRowCounts { + if statementOptions.arrayDMLRowCounts { self.buffer.writeInteger(UInt8(1)) // pointer (al8pidmlrc) self.buffer.writeUB4(1) // al8pidmlrcbl / numberOfExecutions self.buffer.writeInteger(UInt8(1)) // pointer (al8pidmlrcl) @@ -571,15 +571,15 @@ struct OracleFrontendMessageEncoder { self.buffer.writeUB4(0) // number of chunk ids } } - if queryContext.cursorID == 0 || queryContext.statement.isDDL { - queryContext.query.sql + if statementContext.cursorID == 0 || statementContext.type.isDDL { + statementContext.statement.sql ._encodeRaw(into: &self.buffer, context: .default) self.buffer.writeUB4(1) // al8i4[0] parse } else { self.buffer.writeUB4(0) // al8i4[0] parse } - if queryContext.statement.isQuery { - if queryContext.cursorID == 0 { + if statementContext.type.isQuery { + if statementContext.cursorID == 0 { self.buffer.writeUB4(0) // al8i4[1] execution count } else { self.buffer.writeUB4(iterationsCount) @@ -593,42 +593,42 @@ struct OracleFrontendMessageEncoder { self.buffer.writeUB4(0) // al8i4[5] SCN (part 1) self.buffer.writeUB4(0) // al8i4[6] SCN (part 2) self.buffer.writeUB4( - queryContext.statement.isQuery ? 1 : 0 + statementContext.type.isQuery ? 1 : 0 ) // al8i4[7] is query self.buffer.writeUB4(0) // al8i4[8] self.buffer.writeUB4(dmlOptions) // al8i4[9] DML row counts/implicit self.buffer.writeUB4(0) // al8i4[10] self.buffer.writeUB4(0) // al8i4[11] self.buffer.writeUB4(0) // al8i4[12] - if queryContext.requiresDefine { + if statementContext.requiresDefine { guard let columns = describeInfo?.columns else { preconditionFailure() } self.writeColumnMetadata(columns) } else if parametersCount > 0 { - self.writeBindParameters(queryContext.query.binds) + self.writeBindParameters(statementContext.statement.binds) } self.endRequest() } mutating func reexecute( - queryContext: ExtendedQueryContext, cleanupContext: CleanupContext + statementContext: StatementContext, cleanupContext: CleanupContext ) { self.clearIfNeeded() self.startRequest() let functionCode: Constants.FunctionCode - if queryContext.statement.isQuery && !queryContext.requiresDefine - && queryContext.options.prefetchRows > 0 + if statementContext.type.isQuery && !statementContext.requiresDefine + && statementContext.options.prefetchRows > 0 { functionCode = .reexecuteAndFetch } else { functionCode = .reexecute } - let parameters = queryContext.query.binds + let parameters = statementContext.statement.binds var executionFlags1: UInt32 = 0 var executionFlags2: UInt32 = 0 var numberOfIterations: UInt32 = 0 @@ -636,9 +636,9 @@ struct OracleFrontendMessageEncoder { if functionCode == .reexecuteAndFetch { executionFlags1 |= Constants.TNS_EXEC_OPTION_EXECUTE - numberOfIterations = UInt32(queryContext.options.prefetchRows) + numberOfIterations = UInt32(statementContext.options.prefetchRows) } else { - if queryContext.options.autoCommit { + if statementContext.options.autoCommit { executionFlags2 |= Constants.TNS_EXEC_OPTION_COMMIT_REEXECUTE } numberOfIterations = UInt32(numberOfExecutions) @@ -648,7 +648,7 @@ struct OracleFrontendMessageEncoder { self.writeFunctionCode( messageType: .function, functionCode: functionCode ) - self.buffer.writeUB4(UInt32(queryContext.cursorID)) + self.buffer.writeUB4(UInt32(statementContext.cursorID)) self.buffer.writeUB4(numberOfIterations) self.buffer.writeUB4(executionFlags1) self.buffer.writeUB4(executionFlags2) @@ -1072,7 +1072,7 @@ extension OracleFrontendMessageEncoder { } -// MARK: Data/Query related stuff +// MARK: Data/Statement related stuff extension OracleFrontendMessageEncoder { private mutating func writePiggybacks(context: CleanupContext) { diff --git a/Sources/OracleNIO/Messages/OracleBackendMessage+BitVector.swift b/Sources/OracleNIO/Messages/OracleBackendMessage+BitVector.swift index 3c179ee..fdd1ed5 100644 --- a/Sources/OracleNIO/Messages/OracleBackendMessage+BitVector.swift +++ b/Sources/OracleNIO/Messages/OracleBackendMessage+BitVector.swift @@ -25,7 +25,7 @@ extension OracleBackendMessage { let columnsCountSent = try buffer.throwingReadUB2() guard let columnsCount = context.columnsCount else { preconditionFailure( - "How can we receive a bit vector without an active query?" + "How can we receive a bit vector without an active statement?" ) } var length = Int((Double(columnsCount) / 8.0).rounded(.down)) diff --git a/Sources/OracleNIO/Messages/OracleBackendMessage+Parameter.swift b/Sources/OracleNIO/Messages/OracleBackendMessage+Parameter.swift index b08b30c..7999b64 100644 --- a/Sources/OracleNIO/Messages/OracleBackendMessage+Parameter.swift +++ b/Sources/OracleNIO/Messages/OracleBackendMessage+Parameter.swift @@ -112,7 +112,7 @@ extension OracleBackendMessage { { buffer.moveReaderIndex(forwardBy: bytesCount) } - if context.queryOptions!.arrayDMLRowCounts == true { + if context.statementOptions!.arrayDMLRowCounts == true { let numberOfRows = buffer.readUB4() ?? 0 rowCounts = [] for _ in 0.., - result: QueryResult, + result: StatementResult, context: ChannelHandlerContext ) { let rows: OracleRowStream @@ -699,7 +700,7 @@ final class OracleChannelHandler: ChannelDuplexHandler { logger: result.logger ) promise.succeed(rows) - self.run(self.state.readyForQueryReceived(), with: context) + self.run(self.state.readyForStatementReceived(), with: context) } } @@ -804,7 +805,7 @@ extension OracleChannelHandler: OracleRowsDataSource { guard self.rowStream === stream, let handlerContext else { return } - let action = self.state.requestQueryRows() + let action = self.state.requestStatementRows() self.run(action, with: handlerContext) } @@ -812,7 +813,7 @@ extension OracleChannelHandler: OracleRowsDataSource { guard self.rowStream === stream, let handlerContext else { return } - let action = self.state.cancelQueryStream() + let action = self.state.cancelStatementStream() self.run(action, with: handlerContext) } } diff --git a/Sources/OracleNIO/OracleCodable.swift b/Sources/OracleNIO/OracleCodable.swift index a2dab5c..7a6b7ab 100644 --- a/Sources/OracleNIO/OracleCodable.swift +++ b/Sources/OracleNIO/OracleCodable.swift @@ -72,8 +72,8 @@ public protocol OracleThrowingDynamicTypeEncodable: Sendable, Equatable { /// stable between databases. /// /// This is the non-throwing alternative to ``OracleThrowingDynamicTypeEncodable``. It allows -/// users to create ``OracleQuery``s via `ExpressibleByStringInterpolation` without having -/// to spell `try`. +/// users to create ``OracleStatement``s via `ExpressibleByStringInterpolation` without +/// having to spell `try`. public protocol OracleDynamicTypeEncodable: OracleThrowingDynamicTypeEncodable { /// Encode the entity into `buffer`, using the provided `context` as needed, without setting /// the byte count. @@ -115,7 +115,7 @@ extension Array where Element: OracleThrowingDynamicTypeEncodable { /// A type that can encode itself to a oracle wire binary representation. /// /// It enforces that the ``OracleThrowingDynamicTypeEncodable-Implementations`` -/// does not throw. This allows users to create ``OracleQuery``'s using the +/// does not throw. This allows users to create ``OracleStatement``'s using the /// `ExpressibleByStringInterpolation` without having to spell `try`. public protocol OracleEncodable: OracleThrowingEncodable, OracleDynamicTypeEncodable diff --git a/Sources/OracleNIO/OracleEventsHandler.swift b/Sources/OracleNIO/OracleEventsHandler.swift index 5372f88..15aca29 100644 --- a/Sources/OracleNIO/OracleEventsHandler.swift +++ b/Sources/OracleNIO/OracleEventsHandler.swift @@ -24,7 +24,7 @@ enum OracleSQLEvent { ) /// The event that is used to inform upstream handlers that ``OracleChannelHandler`` is /// currently idle. - case readyForQuery + case readyForStatement /// The event that is used to inform state about an ongoing TLS renegotiation. case renegotiateTLS } @@ -65,14 +65,14 @@ final class OracleEventsHandler: ChannelInboundHandler { } self.state = .readyForStartup self.startupDonePromise.succeed((version, sessionID, serialNumber)) - case OracleSQLEvent.readyForQuery: + case OracleSQLEvent.readyForStatement: switch self.state { case .initialized, .connected: preconditionFailure( - "Expected to get a `readyForStartup` before we get a `readyForQuery` event" + "Expected to get a `readyForStartup` before we get a `readyForStatement` event" ) case .readyForStartup: - // for the first time, we are ready to query, this means + // for the first time, we are ready for statements, this means // startup/auth was successful self.state = .authenticated case .authenticated: diff --git a/Sources/OracleNIO/OracleRow.swift b/Sources/OracleNIO/OracleRow.swift index 52a7fe2..059a887 100644 --- a/Sources/OracleNIO/OracleRow.swift +++ b/Sources/OracleNIO/OracleRow.swift @@ -13,7 +13,7 @@ import NIOCore -/// `OracleRow` represents a single table row that is received from the server for a query. +/// `OracleRow` represents a single table row that is received from the server for a statement. /// /// Its element type is ``OracleCell``. /// diff --git a/Sources/OracleNIO/OracleRowStream.swift b/Sources/OracleNIO/OracleRowStream.swift index e3640e4..b304d6f 100644 --- a/Sources/OracleNIO/OracleRowStream.swift +++ b/Sources/OracleNIO/OracleRowStream.swift @@ -14,7 +14,7 @@ import Logging import NIOCore -struct QueryResult { +struct StatementResult { enum Value: Equatable { case noRows case describeInfo([OracleColumn]) diff --git a/Sources/OracleNIO/OracleSQLError.swift b/Sources/OracleNIO/OracleSQLError.swift index 7faba63..05fa206 100644 --- a/Sources/OracleNIO/OracleSQLError.swift +++ b/Sources/OracleNIO/OracleSQLError.swift @@ -30,7 +30,7 @@ public struct OracleSQLError: Sendable, Error { case uncleanShutdown case unexpectedBackendMessage case server - case queryCancelled + case statementCancelled case serverVersionNotSupported case sidNotSupported case missingParameter @@ -55,7 +55,7 @@ public struct OracleSQLError: Sendable, Error { public static let unexpectedBackendMessage = Self(.unexpectedBackendMessage) public static let server = Self(.server) - public static let queryCancelled = Self(.queryCancelled) + public static let statementCancelled = Self(.statementCancelled) public static let serverVersionNotSupported = Self(.serverVersionNotSupported) public static let sidNotSupported = Self(.sidNotSupported) @@ -83,8 +83,8 @@ public struct OracleSQLError: Sendable, Error { return "unexpectedBackendMessage" case .server: return "server" - case .queryCancelled: - return "queryCancelled" + case .statementCancelled: + return "statementCancelled" case .serverVersionNotSupported: return "serverVersionNotSupported" case .sidNotSupported: @@ -148,12 +148,12 @@ public struct OracleSQLError: Sendable, Error { } } - /// The query that failed. - public internal(set) var query: OracleQuery? { - get { self.backing.query } + /// The statement that failed. + public internal(set) var statement: OracleStatement? { + get { self.backing.statement } set { self.copyBackingStorageIfNecessary() - self.backing.query = newValue + self.backing.statement = newValue } } @@ -168,11 +168,11 @@ public struct OracleSQLError: Sendable, Error { } init( - code: Code, query: OracleQuery, + code: Code, statement: OracleStatement, file: String? = nil, line: Int? = nil ) { self.backing = .init(code: code) - self.query = query + self.statement = statement self.file = file self.line = line } @@ -187,7 +187,7 @@ public struct OracleSQLError: Sendable, Error { fileprivate var underlying: Error? fileprivate var file: String? fileprivate var line: Int? - fileprivate var query: OracleQuery? + fileprivate var statement: OracleStatement? fileprivate var backendMessage: OracleBackendMessage? init(code: Code) { @@ -200,7 +200,7 @@ public struct OracleSQLError: Sendable, Error { new.underlying = self.underlying new.file = self.file new.line = self.line - new.query = self.query + new.statement = self.statement new.backendMessage = self.backendMessage return new } @@ -285,7 +285,7 @@ public struct OracleSQLError: Sendable, Error { static let nationalCharsetNotSupported = OracleSQLError(code: .nationalCharsetNotSupported) - static let queryCancelled = OracleSQLError(code: .queryCancelled) + static let statementCancelled = OracleSQLError(code: .statementCancelled) static let serverVersionNotSupported = OracleSQLError(code: .serverVersionNotSupported) @@ -336,8 +336,8 @@ extension OracleSQLError: CustomStringConvertible { } } - if self.query != nil { - result.append(", query: ********") + if self.statement != nil { + result.append(", statement: ********") } result.append(") ") @@ -368,23 +368,23 @@ extension OracleSQLError: CustomDebugStringConvertible { result.append(")") } - if let backendMessage = self.backendMessage { + if let backendMessage { result.append(", backendMessage: \(String(reflecting: backendMessage))") } - if let underlying = self.underlying { + if let underlying { result.append(", underlying: \(String(reflecting: underlying))") } - if let file = self.file { + if let file { result.append(", triggeredFromRequestInFile: \(file)") if let line = self.line { result.append(", line: \(line)") } } - if let query = self.query { - result.append(", query: \(String(reflecting: query))") + if let statement { + result.append(", statement: \(String(reflecting: statement))") } result.append(")") diff --git a/Sources/OracleNIO/OracleQuery.swift b/Sources/OracleNIO/OracleStatement.swift similarity index 95% rename from Sources/OracleNIO/OracleQuery.swift rename to Sources/OracleNIO/OracleStatement.swift index cdda159..bc91e08 100644 --- a/Sources/OracleNIO/OracleQuery.swift +++ b/Sources/OracleNIO/OracleStatement.swift @@ -14,11 +14,15 @@ import NIOConcurrencyHelpers import NIOCore -/// A Oracle SQL query, that can be executed on a Oracle server. Contains the raw sql string and bindings. -public struct OracleQuery: Sendable, Hashable { - /// The query string. +@available(*, deprecated, renamed: "OracleStatement") +public typealias OracleQuery = OracleStatement + +/// A Oracle SQL statement, that can be executed on a Oracle server. +/// Contains the raw sql string and bindings. +public struct OracleStatement: Sendable, Hashable { + /// The statement's string. public var sql: String - /// The query binds. + /// The statement's binds. public var binds: OracleBindings public init( @@ -30,7 +34,7 @@ public struct OracleQuery: Sendable, Hashable { } } -extension OracleQuery: ExpressibleByStringInterpolation { +extension OracleStatement: ExpressibleByStringInterpolation { public init(stringInterpolation: StringInterpolation) { self.sql = stringInterpolation.sql self.binds = stringInterpolation.binds @@ -42,7 +46,7 @@ extension OracleQuery: ExpressibleByStringInterpolation { } } -extension OracleQuery { +extension OracleStatement { public struct StringInterpolation: StringInterpolationProtocol { public typealias StringLiteralType = String @@ -138,15 +142,15 @@ extension OracleQuery { } } -extension OracleQuery: CustomStringConvertible { +extension OracleStatement: CustomStringConvertible { public var description: String { "\(self.sql) \(self.binds)" } } -extension OracleQuery: CustomDebugStringConvertible { +extension OracleStatement: CustomDebugStringConvertible { public var debugDescription: String { - "OracleQuery(sql: \(String(describing: self.sql)), binds: \(String(reflecting: self.binds))" + "OracleStatement(sql: \(String(describing: self.sql)), binds: \(String(reflecting: self.binds))" } } @@ -403,7 +407,7 @@ public struct OracleBindings: Sendable, Hashable { /// results in the out bind. /// /// You might wonder why we don't use the metadata on `OracleRef` itself. - /// This is because we cannot know if the metadata is from a previous query or not. + /// This is because we cannot know if the metadata is from a previous statement or not. func contains(ref: OracleRef) -> String? { self.metadata.first(where: { $0.outContainer === ref })?.bindName } diff --git a/Sources/OracleNIO/OracleTask.swift b/Sources/OracleNIO/OracleTask.swift index f99ae09..6e02722 100644 --- a/Sources/OracleNIO/OracleTask.swift +++ b/Sources/OracleNIO/OracleTask.swift @@ -16,15 +16,15 @@ import NIOCore import RegexBuilder enum OracleTask { - case extendedQuery(ExtendedQueryContext) + case statement(StatementContext) case ping(EventLoopPromise) case commit(EventLoopPromise) case rollback(EventLoopPromise) func failWithError(_ error: OracleSQLError) { switch self { - case .extendedQuery(let context): - switch context.statement { + case .statement(let context): + switch context.type { case .ddl(let promise), .dml(let promise), .plsql(let promise), @@ -43,8 +43,8 @@ enum OracleTask { } } -final class ExtendedQueryContext { - enum Statement { +final class StatementContext { + enum StatementType { case query(EventLoopPromise) case plsql(EventLoopPromise) case dml(EventLoopPromise) @@ -82,9 +82,9 @@ final class ExtendedQueryContext { } } - let statement: Statement - let query: OracleQuery - let options: QueryOptions + let type: StatementType + let statement: OracleStatement + let options: StatementOptions let logger: Logger // metadata @@ -98,50 +98,50 @@ final class ExtendedQueryContext { var sequenceNumber: UInt8 = 2 init( - query: OracleQuery, - options: QueryOptions, + statement: OracleStatement, + options: StatementOptions, logger: Logger, promise: EventLoopPromise ) { self.logger = logger - self.query = query + self.statement = statement self.options = options - self.sqlLength = .init(query.sql.data(using: .utf8)?.count ?? 0) + self.sqlLength = .init(statement.sql.data(using: .utf8)?.count ?? 0) // strip single/multiline comments and and strings from the sql - var sql = query.sql + var sql = statement.sql sql = sql.replacing(/\/\*[\S\n ]+?\*\//, with: "") sql = sql.replacing(/\--.*(\n|$)/, with: "") sql = sql.replacing(/'[^']*'(?=(?:[^']*[^']*')*[^']*$)/, with: "") self.isReturning = - query.binds.metadata + statement.binds.metadata .first(where: \.isReturnBind) != nil - let query = Self.determineStatementType( + let type = Self.determineStatementType( minifiedSQL: sql, promise: promise ) - self.statement = query + self.type = type } init( cursor: Cursor, - options: QueryOptions, + options: StatementOptions, logger: Logger, promise: EventLoopPromise ) { self.logger = logger - self.query = "" + self.statement = "" self.sqlLength = 0 self.cursorID = cursor.id self.options = options self.isReturning = false - self.statement = .cursor(cursor, promise) + self.type = .cursor(cursor, promise) } private static func determineStatementType( minifiedSQL sql: String, promise: EventLoopPromise - ) -> Statement { + ) -> StatementType { var fragment = sql.trimmingCharacters(in: .whitespacesAndNewlines) if fragment.first == "(" { fragment.removeFirst() @@ -166,7 +166,7 @@ final class ExtendedQueryContext { } } -public struct QueryOptions { +public struct StatementOptions { /// Automatically commit every change made to the database. /// /// This happens on the Oracle server side. So it won't cause additional roundtrips to the database. @@ -216,9 +216,9 @@ public struct QueryOptions { /// cannot be fetched inline. internal var fetchLOBs = false - /// Options to pass to a ``OracleQuery`` to tweak its execution. + /// Options to pass to a ``OracleStatement`` to tweak its execution. /// - Parameters: - /// - autoCommit: Automatically commit after execution of the query without needing an + /// - autoCommit: Automatically commit after execution of the statement without needing an /// additional roundtrip. /// - prefetchRows: Indicates how many rows should be fetched with the initial response from /// the database. Refer to ``prefetchRows`` for additional explanation. diff --git a/Sources/OracleNIO/Pool/OracleClient.swift b/Sources/OracleNIO/Pool/OracleClient.swift index 0bc2665..1e8d57f 100644 --- a/Sources/OracleNIO/Pool/OracleClient.swift +++ b/Sources/OracleNIO/Pool/OracleClient.swift @@ -54,8 +54,8 @@ public final class OracleClient: Sendable, Service { /// Create a new `KeepAliveBehavior`. /// - Parameters: - /// - frequency: The amount of time that shall pass before an idle connection runs a keep-alive `query`. - /// Defaults to `30` seconds. + /// - frequency: The amount of time that shall pass before an idle connection runs + /// a keep-alive `statement`. Defaults to `30` seconds. public init(frequency: Duration = .seconds(30)) { self.frequency = frequency } diff --git a/Tests/IntegrationTests/BugReportTests.swift b/Tests/IntegrationTests/BugReportTests.swift index 34a223c..676861b 100644 --- a/Tests/IntegrationTests/BugReportTests.swift +++ b/Tests/IntegrationTests/BugReportTests.swift @@ -19,7 +19,7 @@ import XCTest final class BugReportTests: XCTIntegrationTest { func testRowsFetchFailsWithDecodingError() async throws { - let schema: OracleQuery = """ + let schema: OracleStatement = """ CREATE TABLE EXPORT_TABLE ( buchung_nr varchar2(36 byte), zeittyp_nr number, @@ -37,9 +37,9 @@ final class BugReportTests: XCTIntegrationTest { readonly number ) """ - _ = try? await connection.query("DROP TABLE EXPORT_TABLE") - try await connection.query(schema) - try await connection.query( + _ = try? await connection.execute("DROP TABLE EXPORT_TABLE") + try await connection.execute(schema) + try await connection.execute( """ begin @@ -144,7 +144,7 @@ final class BugReportTests: XCTIntegrationTest { end; """) - let stream = try await connection.query( + let stream = try await connection.execute( """ SELECT buchung_nr , zeittyp_nr diff --git a/Tests/IntegrationTests/CustomTypeTests.swift b/Tests/IntegrationTests/CustomTypeTests.swift index e67adb4..2536f7b 100644 --- a/Tests/IntegrationTests/CustomTypeTests.swift +++ b/Tests/IntegrationTests/CustomTypeTests.swift @@ -19,19 +19,19 @@ import XCTest final class CustomTypeTests: XCTIntegrationTest { func testCustomType() async throws { // create types and scheme - _ = try? await self.connection.query( + _ = try? await self.connection.execute( """ create type udt_SubObject as object ( SubNumberValue number, SubStringValue varchar2(60) ) """) - _ = try? await self.connection.query( + _ = try? await self.connection.execute( """ create type udt_ObjectArray as varray(10) of udt_SubObject """) - _ = try? await self.connection.query( + _ = try? await self.connection.execute( """ create type udt_Object as object ( NumberValue number, @@ -58,9 +58,9 @@ final class CustomTypeTests: XCTIntegrationTest { SubObjectArray udt_ObjectArray ) """) - _ = try? await self.connection.query("create type udt_Array as varray(10) of number") - _ = try? await self.connection.query("drop table TestObjects") - try await self.connection.query( + _ = try? await self.connection.execute("create type udt_Array as varray(10) of number") + _ = try? await self.connection.execute("drop table TestObjects") + try await self.connection.execute( """ create table TestObjects ( IntCol number(9) not null, @@ -70,7 +70,7 @@ final class CustomTypeTests: XCTIntegrationTest { """) // insert samples - try await self.connection.query( + try await self.connection.execute( """ insert into TestObjects values (1, udt_Object(1, 'First row', 'First', 'N First Row', 'N First', @@ -89,12 +89,12 @@ final class CustomTypeTests: XCTIntegrationTest { udt_SubObject(6, 'second element'))), udt_Array(5, 10, null, 20)) """) - try await self.connection.query( + try await self.connection.execute( """ insert into TestObjects values (2, null, udt_Array(3, null, 9, 12, 15)) """) - try await self.connection.query( + try await self.connection.execute( """ insert into TestObjects values (3, udt_Object(3, 'Third row', 'Third', 'N Third Row', 'N Third', @@ -116,7 +116,7 @@ final class CustomTypeTests: XCTIntegrationTest { """) // actual test - let stream = try await self.connection.query( + let stream = try await self.connection.execute( """ select IntCol, ObjectCol, ArrayCol from TestObjects diff --git a/Tests/IntegrationTests/OracleClientTests.swift b/Tests/IntegrationTests/OracleClientTests.swift index a9b9311..f3d0d80 100644 --- a/Tests/IntegrationTests/OracleClientTests.swift +++ b/Tests/IntegrationTests/OracleClientTests.swift @@ -31,7 +31,7 @@ final class OracleClientTests: XCTestCase { taskGroup.addTask { try await client.withConnection { connection in do { - let rows = try await connection.query( + let rows = try await connection.execute( "SELECT 1, 'Timo', 23 FROM dual", logger: .oracleTest) for try await (userID, name, age) in rows.decode( (Int, String, Int).self) @@ -68,7 +68,7 @@ final class OracleClientTests: XCTestCase { taskGroup.addTask { try await client.withConnection { connection in do { - let rows = try await connection.query( + let rows = try await connection.execute( "SELECT 1, 'Timo', 23 FROM dual", logger: .oracleTest) for try await (userID, name, age) in rows.decode( (Int, String, Int).self) @@ -111,7 +111,7 @@ final class OracleClientTests: XCTestCase { for _ in 0.. 255 let idReturnBind = OracleRef(dataType: .number, isReturnBind: true) - let query: OracleQuery = """ + let query: OracleStatement = """ INSERT INTO foo (id, title, something, data) SET (\(id), \(string), \(null), \(buffer)) RETURNING id INTO \(idReturnBind) """ @@ -62,28 +62,28 @@ final class OracleQueryTests: XCTestCase { func testBindsAreStored() throws { let bind = ByteBuffer(bytes: [UInt8](repeating: 42, count: 50)) - let query1: OracleQuery = "INSERT INTO table (col) VALUES \(bind)" + let query1: OracleStatement = "INSERT INTO table (col) VALUES \(bind)" XCTAssertGreaterThan(query1.binds.bytes.readableBytes, 0) XCTAssertEqual(query1.binds.longBytes.readableBytes, 0) - var query2: OracleQuery = "INSERT INTO table (col) VALUES :1" + var query2: OracleStatement = "INSERT INTO table (col) VALUES :1" query2.binds.appendUnprotected(bind, context: .default, bindName: "1") XCTAssertGreaterThan(query2.binds.bytes.readableBytes, 0) XCTAssertEqual(query2.binds.longBytes.readableBytes, 0) let throwingBind = ThrowingByteBuffer(bind) - let query3: OracleQuery = try "INSERT INTO table (col) VALUES \(throwingBind)" + let query3: OracleStatement = try "INSERT INTO table (col) VALUES \(throwingBind)" XCTAssertGreaterThan(query3.binds.bytes.readableBytes, 0) XCTAssertEqual(query3.binds.longBytes.readableBytes, 0) - var query4: OracleQuery = "INSERT INTO table (col) VALUES :1" + var query4: OracleStatement = "INSERT INTO table (col) VALUES :1" try query4.binds.appendUnprotected(throwingBind, context: .default, bindName: "1") XCTAssertGreaterThan(query4.binds.bytes.readableBytes, 0) XCTAssertEqual(query4.binds.longBytes.readableBytes, 0) let bindRef = OracleRef(bind) - let query5: OracleQuery = "INSERT INTO table (col) VALUES \(bindRef)" + let query5: OracleStatement = "INSERT INTO table (col) VALUES \(bindRef)" XCTAssertGreaterThan(query5.binds.bytes.readableBytes, 0) XCTAssertEqual(query5.binds.longBytes.readableBytes, 0) } @@ -91,28 +91,28 @@ final class OracleQueryTests: XCTestCase { func testLongValuesAreStoredInDedicatedBuffer() throws { let long = ByteBuffer(bytes: [UInt8](repeating: 42, count: 50000)) - let query1: OracleQuery = "INSERT INTO table (col) VALUES \(long)" + let query1: OracleStatement = "INSERT INTO table (col) VALUES \(long)" XCTAssertEqual(query1.binds.bytes.readableBytes, 0) XCTAssertGreaterThan(query1.binds.longBytes.readableBytes, 0) - var query2: OracleQuery = "INSERT INTO table (col) VALUES :1" + var query2: OracleStatement = "INSERT INTO table (col) VALUES :1" query2.binds.appendUnprotected(long, context: .default, bindName: "1") XCTAssertEqual(query2.binds.bytes.readableBytes, 0) XCTAssertGreaterThan(query2.binds.longBytes.readableBytes, 0) let throwingLong = ThrowingByteBuffer(long) - let query3: OracleQuery = try "INSERT INTO table (col) VALUES \(throwingLong)" + let query3: OracleStatement = try "INSERT INTO table (col) VALUES \(throwingLong)" XCTAssertEqual(query3.binds.bytes.readableBytes, 0) XCTAssertGreaterThan(query3.binds.longBytes.readableBytes, 0) - var query4: OracleQuery = "INSERT INTO table (col) VALUES :1" + var query4: OracleStatement = "INSERT INTO table (col) VALUES :1" try query4.binds.appendUnprotected(throwingLong, context: .default, bindName: "1") XCTAssertEqual(query4.binds.bytes.readableBytes, 0) XCTAssertGreaterThan(query4.binds.longBytes.readableBytes, 0) let longRef = OracleRef(long) - let query5: OracleQuery = "INSERT INTO table (col) VALUES \(longRef)" + let query5: OracleStatement = "INSERT INTO table (col) VALUES \(longRef)" XCTAssertEqual(query5.binds.bytes.readableBytes, 0) XCTAssertGreaterThan(query5.binds.longBytes.readableBytes, 0) } diff --git a/Tests/OracleNIOTests/OracleQueryContextTests.swift b/Tests/OracleNIOTests/StatementContextTests.swift similarity index 68% rename from Tests/OracleNIOTests/OracleQueryContextTests.swift rename to Tests/OracleNIOTests/StatementContextTests.swift index b1fa1c2..179b1ff 100644 --- a/Tests/OracleNIOTests/OracleQueryContextTests.swift +++ b/Tests/OracleNIOTests/StatementContextTests.swift @@ -16,21 +16,24 @@ import XCTest @testable import OracleNIO -final class OracleQueryContextTests: XCTestCase { +final class StatementContextTests: XCTestCase { func testStatementWithSpaceSeparator() { - var context: ExtendedQueryContext? + var context: StatementContext? defer { context?.cleanup() } - XCTAssertNoThrow(context = ExtendedQueryContext(query: "SELECT any FROM any")) - XCTAssertEqual(context?.statement.isQuery, true) + XCTAssertNoThrow( + context = StatementContext( + statement: "SELECT any FROM any") + ) + XCTAssertEqual(context?.type.isQuery, true) } func testStatementWithNewlineSeparator() { - var context: ExtendedQueryContext? + var context: StatementContext? defer { context?.cleanup() } XCTAssertNoThrow( - context = ExtendedQueryContext( - query: """ + context = StatementContext( + statement: """ SELECT any, any2, @@ -38,30 +41,30 @@ final class OracleQueryContextTests: XCTestCase { FROM any """)) - XCTAssertEqual(context?.statement.isQuery, true) + XCTAssertEqual(context?.type.isQuery, true) } func testQueryWithSingleLineComments() { - var context: ExtendedQueryContext? + var context: StatementContext? defer { context?.cleanup() } XCTAssertNoThrow( - context = ExtendedQueryContext( - query: """ + context = StatementContext( + statement: """ -- hello there SELECT any, any2, -- hello again any3 FROM any -- goodby """)) - XCTAssertEqual(context?.statement.isQuery, true) + XCTAssertEqual(context?.type.isQuery, true) } func testQueryWithMultiLineComments() { - var context: ExtendedQueryContext? + var context: StatementContext? defer { context?.cleanup() } XCTAssertNoThrow( - context = ExtendedQueryContext( - query: """ + context = StatementContext( + statement: """ /* Hello there */ SELECT any, any2, -- I'm sneaky @@ -71,7 +74,7 @@ final class OracleQueryContextTests: XCTestCase { */ any3 FROM any --bye """)) - XCTAssertEqual(context?.statement.isQuery, true) + XCTAssertEqual(context?.type.isQuery, true) } } diff --git a/Tests/OracleNIOTests/Utils/ConnectionAction+TestUtils.swift b/Tests/OracleNIOTests/Utils/ConnectionAction+TestUtils.swift index 45a5cc5..fd236e7 100644 --- a/Tests/OracleNIOTests/Utils/ConnectionAction+TestUtils.swift +++ b/Tests/OracleNIOTests/Utils/ConnectionAction+TestUtils.swift @@ -14,8 +14,8 @@ @testable import OracleNIO extension ConnectionStateMachine { - static func readyForQuery() -> Self { - ConnectionStateMachine(.readyForQuery) + static func readyForStatement() -> Self { + ConnectionStateMachine(.readyForStatement) } } @@ -37,7 +37,7 @@ extension ConnectionStateMachine.ConnectionAction: Equatable { return lhs?.futureResult === rhs?.futureResult case (.fireChannelInactive, .fireChannelInactive): return true - case (.fireEventReadyForQuery, .fireEventReadyForQuery): + case (.fireEventReadyForStatement, .fireEventReadyForStatement): return true case (.closeConnectionAndCleanup(let lhs), .closeConnectionAndCleanup(let rhs)): @@ -100,14 +100,14 @@ extension ConnectionStateMachine.ConnectionAction: Equatable { case (.sendFlushOutBinds, .sendFlushOutBinds): return true case ( - .failQuery(let lhsPromise, let lhsError, let lhsCleanup), - .failQuery(let rhsPromise, let rhsError, let rhsCleanup) + .failStatement(let lhsPromise, let lhsError, let lhsCleanup), + .failStatement(let rhsPromise, let rhsError, let rhsCleanup) ): return lhsPromise.futureResult === rhsPromise.futureResult && lhsError == rhsError && lhsCleanup == rhsCleanup case ( - .succeedQuery(let lhsPromise, let lhsResult), - .succeedQuery(let rhsPromise, let rhsResult) + .succeedStatement(let lhsPromise, let lhsResult), + .succeedStatement(let rhsPromise, let rhsResult) ): return lhsPromise.futureResult === rhsPromise.futureResult && lhsResult.value == rhsResult.value @@ -156,7 +156,7 @@ extension OracleSQLError: Equatable { extension OracleTask: Equatable { public static func == (lhs: OracleTask, rhs: OracleTask) -> Bool { switch (lhs, rhs) { - case (.extendedQuery(let lhs), .extendedQuery(let rhs)): + case (.statement(let lhs), .statement(let rhs)): return lhs === rhs case (.ping, .ping): return true @@ -177,8 +177,8 @@ extension CleanupContext: Equatable { } } -extension ExtendedQueryContext: Equatable { - public static func == (lhs: ExtendedQueryContext, rhs: ExtendedQueryContext) -> Bool { +extension StatementContext: Equatable { + public static func == (lhs: StatementContext, rhs: StatementContext) -> Bool { lhs === rhs } } diff --git a/Tests/OracleNIOTests/Utils/ExtendedQueryContext+TestUtils.swift b/Tests/OracleNIOTests/Utils/ExtendedQueryContext+TestUtils.swift index 43568f7..7521657 100644 --- a/Tests/OracleNIOTests/Utils/ExtendedQueryContext+TestUtils.swift +++ b/Tests/OracleNIOTests/Utils/ExtendedQueryContext+TestUtils.swift @@ -16,21 +16,21 @@ import NIOEmbedded @testable import OracleNIO -extension ExtendedQueryContext { +extension StatementContext { convenience init( - query: OracleQuery, + statement: OracleStatement, promise: EventLoopPromise = EmbeddedEventLoop().makePromise() ) { self.init( - query: query, options: .init(), + statement: statement, options: .init(), logger: OracleConnection.noopLogger, promise: promise ) } func cleanup() { - switch self.statement { + switch self.type { case .query(let promise), .plsql(let promise), .dml(let promise), diff --git a/Tests/OracleNIOTests/Utils/QueryResult+TestUtils.swift b/Tests/OracleNIOTests/Utils/QueryResult+TestUtils.swift index 5b80bb7..dc80ab5 100644 --- a/Tests/OracleNIOTests/Utils/QueryResult+TestUtils.swift +++ b/Tests/OracleNIOTests/Utils/QueryResult+TestUtils.swift @@ -16,7 +16,7 @@ import NIOEmbedded @testable import OracleNIO -extension QueryResult { +extension StatementResult { init(value: Value) { self.init(value: value, logger: OracleConnection.noopLogger) } From 80963902048cd2eb05c210e9526b57aa52f3d9d0 Mon Sep 17 00:00:00 2001 From: Timo <38291523+lovetodream@users.noreply.github.com> Date: Wed, 19 Jun 2024 12:15:53 +0200 Subject: [PATCH 2/3] refactor: add dedicated file for deprecations --- .../Connection/OracleConnection.swift | 17 ------ Sources/OracleNIO/Deprecations.swift | 61 +++++++++++++++++++ Sources/OracleNIO/OracleStatement.swift | 3 - 3 files changed, 61 insertions(+), 20 deletions(-) create mode 100644 Sources/OracleNIO/Deprecations.swift diff --git a/Sources/OracleNIO/Connection/OracleConnection.swift b/Sources/OracleNIO/Connection/OracleConnection.swift index d1bc043..d203593 100644 --- a/Sources/OracleNIO/Connection/OracleConnection.swift +++ b/Sources/OracleNIO/Connection/OracleConnection.swift @@ -408,23 +408,6 @@ extension OracleConnection { try await self.rollback().get() } - @available(*, deprecated, renamed: "execute(_:options:logger:file:line:)") - @discardableResult - public func query( - _ query: OracleQuery, - options: StatementOptions = .init(), - logger: Logger? = nil, - file: String = #fileID, line: Int = #line - ) async throws -> OracleRowSequence { - try await self.execute( - query, - options: options, - logger: logger, - file: file, - line: line - ) - } - /// Run a statement on the Oracle server the connection is connected to. /// /// - Parameters: diff --git a/Sources/OracleNIO/Deprecations.swift b/Sources/OracleNIO/Deprecations.swift new file mode 100644 index 0000000..c5a5d0e --- /dev/null +++ b/Sources/OracleNIO/Deprecations.swift @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the OracleNIO open source project +// +// Copyright (c) 2024 Timo Zacherl and the OracleNIO project authors +// Licensed under Apache License v2.0 +// +// See LICENSE for license information +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +// TODO: remove them all before 1.0.0 + +import struct Logging.Logger + +@_documentation(visibility: internal) +@available(*, deprecated, renamed: "OracleStatement") +public typealias OracleQuery = OracleStatement + +@_documentation(visibility: internal) +@available(*, deprecated, renamed: "StatementOptions") +public typealias QueryOptions = StatementOptions + +extension OracleSQLError.Code { + @_documentation(visibility: internal) + @available(*, deprecated, renamed: "statementCancelled") + public static let queryCancelled = OracleSQLError.Code.statementCancelled +} + +extension OracleSQLError { + @_documentation(visibility: internal) + @available(*, deprecated, renamed: "statement", message: "will be removed before 1.0.0") + public internal(set) var query: OracleQuery? { + get { self.statement } + set { + self.statement = newValue + } + } +} + +extension OracleConnection { + @_documentation(visibility: internal) + @available(*, deprecated, renamed: "execute(_:options:logger:file:line:)") + @discardableResult + public func query( + _ query: OracleQuery, + options: QueryOptions = .init(), + logger: Logger? = nil, + file: String = #fileID, line: Int = #line + ) async throws -> OracleRowSequence { + try await self.execute( + query, + options: options, + logger: logger, + file: file, + line: line + ) + } +} diff --git a/Sources/OracleNIO/OracleStatement.swift b/Sources/OracleNIO/OracleStatement.swift index bc91e08..722925b 100644 --- a/Sources/OracleNIO/OracleStatement.swift +++ b/Sources/OracleNIO/OracleStatement.swift @@ -14,9 +14,6 @@ import NIOConcurrencyHelpers import NIOCore -@available(*, deprecated, renamed: "OracleStatement") -public typealias OracleQuery = OracleStatement - /// A Oracle SQL statement, that can be executed on a Oracle server. /// Contains the raw sql string and bindings. public struct OracleStatement: Sendable, Hashable { From 2c4ea5db98813485a43816416c2d294ed9b15ea0 Mon Sep 17 00:00:00 2001 From: Timo <38291523+lovetodream@users.noreply.github.com> Date: Wed, 19 Jun 2024 13:21:31 +0200 Subject: [PATCH 3/3] chore: soundness stuff --- Sources/OracleNIO/Deprecations.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/OracleNIO/Deprecations.swift b/Sources/OracleNIO/Deprecations.swift index c5a5d0e..4fbf966 100644 --- a/Sources/OracleNIO/Deprecations.swift +++ b/Sources/OracleNIO/Deprecations.swift @@ -15,22 +15,22 @@ import struct Logging.Logger -@_documentation(visibility: internal) +@_documentation(visibility:internal) @available(*, deprecated, renamed: "OracleStatement") public typealias OracleQuery = OracleStatement -@_documentation(visibility: internal) +@_documentation(visibility:internal) @available(*, deprecated, renamed: "StatementOptions") public typealias QueryOptions = StatementOptions extension OracleSQLError.Code { - @_documentation(visibility: internal) + @_documentation(visibility:internal) @available(*, deprecated, renamed: "statementCancelled") public static let queryCancelled = OracleSQLError.Code.statementCancelled } extension OracleSQLError { - @_documentation(visibility: internal) + @_documentation(visibility:internal) @available(*, deprecated, renamed: "statement", message: "will be removed before 1.0.0") public internal(set) var query: OracleQuery? { get { self.statement } @@ -41,7 +41,7 @@ extension OracleSQLError { } extension OracleConnection { - @_documentation(visibility: internal) + @_documentation(visibility:internal) @available(*, deprecated, renamed: "execute(_:options:logger:file:line:)") @discardableResult public func query(