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

Add @Statement macro #60

Merged
merged 9 commits into from
Oct 2, 2024
Merged

Add @Statement macro #60

merged 9 commits into from
Oct 2, 2024

Conversation

lovetodream
Copy link
Owner

@lovetodream lovetodream commented Sep 30, 2024

Declaration

This adds a protocol OraclePreparedStatement, to allow creating and executing SQL statements in a structured way.

struct UsersStatement: OraclePreparedStatement {
    static let sql = "SELECT id, name, age FROM users WHERE :1 < age"

    typealias Row = (UUID, String, Int)

    var age: Int

    func makeBindings() -> OracleBindings {
        var bindings = OracleBindings()
        bindings.append(age, context: .default, bindName: "1")
        return bindings
    }

    func decodeRow(_ row: OracleRow) throws -> Row {
        try row.decode(Row.self)
    }
}

Declare using Macro

Furthermore it allows us to create these using a powerful @Statement macro.
Macros are not part of the OracleNIO module, the OracleNIOMacros module has to be imported by the caller.
The same code as the one above can be created using two lines of code.

import OracleNIOMacros

@Statement("SELECT \("id", Int.self), \("name", String.self), \("age", Int.self) FROM users WHERE \(bind: "age", OracleNumber.self) < age")
struct UsersStatement {}

The macro adds even more convenience, it expands to the following code:

struct UsersStatement {
    struct Row {
        var id: Int
        var name: String
        var age: Int
    }

    static let sql = "SELECT id, name, age FROM users WHERE :1 < age"
    
    var age: OracleNumber

    func makeBindings() throws -> OracleBindings {
        var bindings = OracleBindings(capacity: 1)
        bindings.append(age, context: .default, bindName: "1")
        return bindings
    }

    func decodeRow(_ row: OracleRow) throws -> Row {
        let (id, name, age) = try row.decode((Int, String, Int).self)
        return Row(id: id, name: name, age: age)
    }
}
extension UsersStatement: OraclePreparedStatement {}

All this is happening at compile time and adds no overhead to the actual queries being performed.

Querying

The statement can now be used similar to normal OracleStatements.

let connection: OracleConnection = ...
let stream = try await connection.execute(UsersStatement(age: 18))
for try await user in stream {
    print(user.id, user.name, user.age)
}

@lovetodream lovetodream added the semver-minor Adds new public API. label Sep 30, 2024
Copy link

codecov bot commented Sep 30, 2024

Codecov Report

Attention: Patch coverage is 95.66631% with 40 lines in your changes missing coverage. Please review.

Project coverage is 87.62%. Comparing base (7fee785) to head (85e00cf).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
Sources/OracleNIO/OracleStatement.swift 18.75% 13 Missing ⚠️
Sources/OracleNIOMacrosPlugin/StatementMacro.swift 97.09% 13 Missing ⚠️
Sources/OracleNIO/OraclePreparedStatement.swift 0.00% 8 Missing ⚠️
.../OracleNIOMacrosPlugin/OracleNIOMacrosPlugin.swift 0.00% 3 Missing ⚠️
...ests/IntegrationTests/PreparedStatementTests.swift 90.90% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #60      +/-   ##
==========================================
+ Coverage   84.88%   87.62%   +2.74%     
==========================================
  Files         155      146       -9     
  Lines       21496    20271    -1225     
==========================================
- Hits        18246    17763     -483     
+ Misses       3250     2508     -742     
Flag Coverage Δ
?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...ources/OracleNIO/Connection/OracleConnection.swift 94.06% <100.00%> (+0.12%) ⬆️
Sources/OracleNIO/Data/LOB.swift 99.60% <ø> (ø)
Sources/OracleNIO/OracleCell.swift 0.00% <ø> (ø)
Sources/OracleNIO/OracleCodable.swift 63.33% <ø> (ø)
Sources/OracleNIO/Pool/OracleClient.swift 100.00% <ø> (ø)
Sources/OracleNIO/Pool/OracleClientMetrics.swift 50.70% <ø> (ø)
...acleNIOMacrosTests/OracleStatementMacroTests.swift 100.00% <100.00%> (ø)
.../OracleNIOMacrosPlugin/OracleNIOMacrosPlugin.swift 0.00% <0.00%> (ø)
...ests/IntegrationTests/PreparedStatementTests.swift 90.90% <90.90%> (ø)
Sources/OracleNIO/OraclePreparedStatement.swift 0.00% <0.00%> (ø)
... and 2 more

... and 19 files with indirect coverage changes

@Tommynocker
Copy link

Tommynocker commented Oct 2, 2024

How I import OracleNIOMacros in swift? I have never worked with macros before

@lovetodream
Copy link
Owner Author

Currently you have to go to your Package.swift and change the version of oracle-nio to this branch and add OracleNIOMacros to the dependencies of your target.

dependencies: [
    .package(url: "https://github.com/lovetodream/oracle-nio.git", branch: "statement-macro")
],
targets: [
    .target(
        name: "PostgresNIOMacros",
        dependencies: [
            ...
            .product(name: "OracleNIO", package: "oracle-nio")
            .product(name: "OracleNIOMacros", package: "oracle-nio")
        ]
    )
]

Then you can use OracleNIOMacros as you would use anything else, just import it on the top of your file.

Note: It is still in development and might contain various bugs/not compile at all for 5.x Swift versions!

@Tommynocker
Copy link

Is there an example Project anywhere ?

@lovetodream
Copy link
Owner Author

Is there an example Project anywhere ?

Not yet, it is still subject to change

@lovetodream lovetodream merged commit 3d889f8 into main Oct 2, 2024
7 of 8 checks passed
@lovetodream lovetodream deleted the statement-macro branch October 2, 2024 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver-minor Adds new public API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants