At ServerSideSwift @fabianfett pitched me the idea to use a Macro for Prepared Statements in PostgresNIO.
The @Statement
macro can generate a prepared statement with columns and binds by providing a slightly modified SQL statement.
import PostgresNIO
import PostgresNIOMacros
@Statement("SELECT \("id", UUID.self), \("name", String.self), \("age", Int.self) FROM users WHERE \(bind: "age", Int.self) < age")
struct UsersStatement {}
let connection: PostgresConnection = ...
let stream = try await connection.execute(UsersStatement(age: 18), logger: ...)
for try await user in stream {
print(user.id, user.name, user.age)
}
Under the hood, the macro generates a prepared statement, in this case, it creates the SQL string SELECT id, name, age FROM users WHERE $1 < age
, a var age: Int
bind and a struct Row
.
struct Row {
var id: UUID
var name: String
var age: Int
}
To achieve the same result without the macro one would need to write the following code:
struct UsersStatement: PostgresPreparedStatement {
struct Row {
var id: UUID
var name: String
var age: Int
}
static let sql = "SELECT id, name, age FROM users WHERE $1 < age"
var age: Int
func makeBindings() throws -> PostgresBindings {
var bindings = PostgresBindings(capacity: 1)
bindings.append(age)
return bindings
}
func decodeRow(_ row: PostgresRow) throws -> Row {
let (id, name, age) = try row.decode((UUID, String, Int).self)
return Row(id: id, name: name, age: age)
}
}
You can try it out here:
If proven useful it might be added to PostgresNIO
in a future release.
Let us know what you think of it!