Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename OracleQuery and friends to OracleStatement #35

Merged
merged 3 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -165,24 +165,24 @@ 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):

```swift
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

Expand Down
2 changes: 1 addition & 1 deletion Snippets/OracleConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 21 additions & 21 deletions Sources/OracleNIO/Connection/OracleConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
///
Expand Down Expand Up @@ -408,22 +408,22 @@ extension OracleConnection {
try await self.rollback().get()
}

/// Run a query on the Oracle server the connection is connected to.
/// Run a statement 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.
/// - 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 query(
_ query: OracleQuery,
options: QueryOptions = .init(),
public func execute(
_ statement: OracleStatement,
options: StatementOptions = .init(),
logger: Logger? = nil,
file: String = #fileID, line: Int = #line
) async throws -> OracleRowSequence {
Expand All @@ -434,14 +434,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
Expand All @@ -450,14 +450,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 {
Expand All @@ -468,14 +468,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
Expand Down
Loading
Loading