Skip to content

lovetodream/oracle-nio

OracleNIO

Supported Swift Versions Supported Platforms SSWG Sandbox Incubating Badge Documentation Apache 2.0 License codecov CI 23ai CI 21c CI ADB

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 a Query, DML, DDL or even PlSQL.

The statement will return a OracleRowSequence, which is an AsyncSequence of OracleRows. 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

Apache 2.0

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.

About

Non-blocking, event-driven Swift client for Oracle DBs. Does not require OCI or ODPI.

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published