Awesome
OracleNIO
Non-blocking, event-driven Swift client for Oracle Databases built on SwiftNIO.
It's like PostgresNIO, but written for Oracle Databases.
Features
- An
OracleConnection
which allows you to connect to, authorize with, query, and retrieve results from an Oracle database server - An async/await interface that supports backpressure
- Automatic conversions between Swift primitive types and the Oracle wire format
- Integrated with the Swift server ecosystem, including use of swift-log.
- Designed to run efficiently on all supported platforms (tested on Linux and Darwin systems)
- Support for
Network.framework
when available (e.g. on Apple platforms) - An
OracleClient
ConnectionPool backed by DRCP (Database Resident Connection Pooling) if available
Supported Oracle Database versions
Oracle Database 12.1 or later.
Connection methods
- Username and password with service name
- Username and password with sid
- Oracle Cloud Infrastructure (OCI) Identity and Access Management (IAM) token based authentication with service name
- Open Authorization (OAuth 2.0) token based authentication with service name
- Open Authorization (OAuth 2.0) token based authentication with sid
Please note that all token based authentication methods are currently untested, because I (@lovetodream) do not have the infrastructure to test this. Contributions are welcome!
All connections can be TLS encrypted using OracleConnection.Configuration.TLS
.
API Docs
Check out the OracleNIO API docs for a detailed look at all of the classes, structs, protocols, and more.
Getting started
Adding the dependency
Add OracleNIO
as a dependency to your Package.swift
:
dependencies: [
.package(url: "https://github.com/lovetodream/oracle-nio.git", from: "1.0.0-alpha"),
...
]
Add OracleNIO
to the target you want to use it in:
targets: [
.target(name: "MyFancyTarget", dependencies: [
.product(name: "OracleNIO", package: "oracle-nio"),
])
]
Creating a connection
To create a connection, first create a connection configuration object:
import OracleNIO
let config = OracleConnection.Configuration(
host: "127.0.0.1",
port: 1521,
service: .serviceName("my_service"), // or .sid("sid")
username: "my_username",
password: "my_password"
)
To create a connection we need a Logger
, that is used to log connection background events.
import Logging
let logger = Logger(label: "oracle-logger")
Now we can put it together:
import OracleNIO
import Logging
let logger = Logger(label: "oracle-logger")
let config = OracleConnection.Configuration(
host: "127.0.0.1",
port: 1521,
service: .serviceName("my_service"),
username: "my_username",
password: "my_password"
)
let connection = try await OracleConnection.connect(
configuration: config,
id: 1,
logger: logger
)
// Close your connection once done
try await connection.close()
Running SQL statements
Once a connection is established, statements can be sent to the server. This is very straightforward:
let rows = try await connection.execute("SELECT id, username, birthday FROM users", logger: logger)
execute(_:options:logger:file:line:)
can run either aQuery
,DML
,DDL
or evenPlSQL
.
The statement will return a OracleRowSequence
, which is an AsyncSequence
of OracleRow
s. The rows can be iterated one-by-one:
for try await row in rows {
// do something with the row
}
Decoding from OracleRow
However, in most cases it is much easier to request a row's fields as a set of Swift types:
for try await (id, username, birthday) in rows.decode((Int, String, Date).self) {
// do something with the datatypes.
}
A type must implement the OracleDecodable
protocol in order to be decoded from a row. OracleNIO
provides default implementations for most of Swift's builtin types, as well as some types provided by Foundation:
Bool
Bytes
,ByteBuffer
,Data
Date
UInt8
,Int8
,UInt16
,Int16
,UInt32
,Int32
,UInt64
,Int64
,UInt
,Int
Float
,Double
String
UUID
OracleNIO
does provide some types which are more specific to Oracle too.
Cursor
IntervalDS
OracleNumber
OracleVectorBinary
,OracleVectorInt8
,OracleVectorFloat32
,OracleVectorFloat64
OracleJSON
RowID
Statements with parameters
Sending parameterized queries to the database is also supported (in the coolest way possible):
let id = 1
let username = "fancyuser"
let birthday = Date()
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 😱, 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 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
SemVer changes are documented for each release on the releases page.
Swift on Server Ecosystem
Oracle NIO is part of the Swift on Server Working Group ecosystem - currently recommended as Sandbox Maturity.
Proposal | Pitch | Review | Vote |
---|---|---|---|
SSWG-0028 | 2023-12-20 | 2024-01-17 | 2024-04-07 |
Language and Platform Support
Any given release of Oracle NIO will support at least the latest version of Swift on a given platform plus 1 previous version, at the time of the release.
Major version releases will be scheduled around official Swift releases, taking no longer 3 months from the Swift release.
Major version releases will drop support for any version of Swift older than the last 2 Swift versions.
This policy is to balance the desire for as much backwards compatibility as possible, while also being able to take advantage of new Swift features for the best API design possible.
License
Copyright (c) 2023-present, Timo Zacherl (@lovetodream)
This project contains code written by others not affliated with this project. All copyright claims are reserved by them. For a full list, with their claimed rights, see NOTICE.txt
Oracle is a registered trademark of Oracle Corporation. Any use of their trademark is under the established trademark guidelines and does not imply any affiliation with or endorsement by them, and all rights are reserved by them.
Swift is a registered trademark of Apple, Inc. Any use of their trademark does not imply any affiliation with or endorsement by them, and all rights are reserved by them.