From 8bc2505cc941c0ce75db32aeec2f8689ca225b63 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Thu, 22 Feb 2024 16:17:29 +0100 Subject: [PATCH 1/2] use 2 space indentation in README --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b6cecc2d..6473bb69 100644 --- a/README.md +++ b/README.md @@ -91,14 +91,14 @@ let client = PostgresClient(configuration: config) Once you have create your client, you must [`run()`] it: ```swift await withTaskGroup(of: Void.self) { taskGroup in - taskGroup.addTask { - await client.run() // !important - } + taskGroup.addTask { + await client.run() // !important + } - // You can use the client while the `client.run()` method is not cancelled. + // You can use the client while the `client.run()` method is not cancelled. - // To shutdown the client, cancel its run method, by cancelling the taskGroup. - taskGroup.cancelAll() + // To shutdown the client, cancel its run method, by cancelling the taskGroup. + taskGroup.cancelAll() } ``` From 580db2d5baa1c83a36ae87b03fda9cec3b9f84a6 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Fri, 19 Apr 2024 11:03:06 +0200 Subject: [PATCH 2/2] More stuff --- Snippets/PostgresQuery-unescaped.swift | 9 +++ Snippets/PostgresQuery.swift | 9 +++ Sources/PostgresNIO/Docs.docc/deprecated.md | 2 +- Sources/PostgresNIO/Docs.docc/index.md | 1 - .../PostgresNIO/Docs.docc/running-queries.md | 12 +++- Sources/PostgresNIO/New/PostgresQuery.swift | 57 ++++++++++++++++++- Tests/IntegrationTests/AsyncTests.swift | 2 +- 7 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 Snippets/PostgresQuery-unescaped.swift create mode 100644 Snippets/PostgresQuery.swift diff --git a/Snippets/PostgresQuery-unescaped.swift b/Snippets/PostgresQuery-unescaped.swift new file mode 100644 index 00000000..0083acff --- /dev/null +++ b/Snippets/PostgresQuery-unescaped.swift @@ -0,0 +1,9 @@ +import PostgresNIO + +// snippet.unescaped +let id = 10000 +let tableName = "users" +let query: PostgresQuery = """ + SELECT id, username, birthday FROM \(unescaped: tableName) WHERE id < \(id); + """ +// snippet.end diff --git a/Snippets/PostgresQuery.swift b/Snippets/PostgresQuery.swift new file mode 100644 index 00000000..d347b069 --- /dev/null +++ b/Snippets/PostgresQuery.swift @@ -0,0 +1,9 @@ +import PostgresNIO + +// snippet.select1 +let id = 10000 +let query: PostgresQuery = """ + SELECT id, username, birthday FROM users WHERE id < \(id); + """ +// snippet.end + diff --git a/Sources/PostgresNIO/Docs.docc/deprecated.md b/Sources/PostgresNIO/Docs.docc/deprecated.md index a29465f6..81171695 100644 --- a/Sources/PostgresNIO/Docs.docc/deprecated.md +++ b/Sources/PostgresNIO/Docs.docc/deprecated.md @@ -9,7 +9,7 @@ have changed. The introduction of structured concurrency changed what developers modern Swift library. Because of this ``PostgresNIO`` added various APIs that embrace the new Swift patterns. This means however, that PostgresNIO still offers APIs that have fallen out of favor. Those are documented here. All those APIs will be removed once the maintainers release the next -major version. The maintainers recommend all adopters to move of those APIs sooner rather than +major version. The maintainers recommend all adopters to move off those APIs sooner rather than later. ## Topics diff --git a/Sources/PostgresNIO/Docs.docc/index.md b/Sources/PostgresNIO/Docs.docc/index.md index 6355a7a4..f796b9df 100644 --- a/Sources/PostgresNIO/Docs.docc/index.md +++ b/Sources/PostgresNIO/Docs.docc/index.md @@ -34,7 +34,6 @@ configure ``PostgresConnection`` to use `Network.framework` as the underlying tr ### Essentials - ``PostgresClient`` -- ``PostgresClient/Configuration`` - ``PostgresConnection`` - diff --git a/Sources/PostgresNIO/Docs.docc/running-queries.md b/Sources/PostgresNIO/Docs.docc/running-queries.md index b2c4586f..586acb7e 100644 --- a/Sources/PostgresNIO/Docs.docc/running-queries.md +++ b/Sources/PostgresNIO/Docs.docc/running-queries.md @@ -1,16 +1,24 @@ -# Running Queries +# Running Statements Interact with the PostgreSQL database by running Queries. ## Overview +You run statements on a PostgreSQL database, by: +1. Creating a connection to the Postgres database by creating a ``PostgresClient`` or + ``PostgresConnection`` You interact with the Postgres database by running SQL [Queries]. +A ``PostgresQuery`` consists out of the ``PostgresQuery/sql`` statement and the +``PostgresQuery/binds``, which are the parameters for the sql statement. + +Users should create ``PostgresQuery``s in most cases through String interpolation: + +@Snippet(path: "postgres-nio/Snippets/PostgresQuery", slice: "select1") -``PostgresQuery`` conforms to ## Topics diff --git a/Sources/PostgresNIO/New/PostgresQuery.swift b/Sources/PostgresNIO/New/PostgresQuery.swift index 1cfcf2dc..0f737fcb 100644 --- a/Sources/PostgresNIO/New/PostgresQuery.swift +++ b/Sources/PostgresNIO/New/PostgresQuery.swift @@ -1,12 +1,62 @@ import NIOCore -/// A Postgres SQL query, that can be executed on a Postgres server. Contains the raw sql string and bindings. +/// A PostgreSQL statement, that can be executed on a database. Contains the raw ``PostgresQuery/sql`` statement +/// and ``PostgresQuery/binds``, that are the parameters for the statement. +/// +/// ## Creating a Query +/// +/// #### Using string interpolation +/// +/// Users should create ``PostgresQuery``s in most cases through string interpolation: +/// +/// @Snippet(path: "postgres-nio/Snippets/PostgresQuery", slice: "select1") +/// +/// While this looks at first glance like a classic case of [SQL injection](https://en.wikipedia.org/wiki/SQL_injection) +/// 😱, PostgresNIO ensures that this usage is safe. +/// The reason for this is, that ``PostgresQuery`` implements Swift's `ExpressibleByStringInterpolation` +/// protocol. ``PostgresQuery`` 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 ``PostgresEncodable`` protocol may be interpolated +/// in this way. +/// +/// ###### Interpolating non parameter values +/// +/// Sometimes you need to interpolate parts of your query that can not be send to the server as an SQL binding. An example could +/// be the table name. In those cases add the `\(unescaped:)` keyword in front of your value. +/// +/// @Snippet(path: "postgres-nio/Snippets/PostgresQuery-unescaped", slice: "unescaped") +/// +/// > Warning: +/// Always make sure, that values passed via `\(unescaped:)` interpolations are trusted. Passing untrusted values can allow +/// [SQL injection](https://en.wikipedia.org/wiki/SQL_injection). +/// +/// #### Manually creating a PostgresQuery +/// +/// ``PostgresQuery`` can be created manually using the ``PostgresQuery/init(unsafeSQL:binds:)`` initializer. +/// In those cases ``PostgresQuery`` will not perform any validation on the provided SQL statement. Users must make sure +/// that their SQL is safe to execute. public struct PostgresQuery: Sendable, Hashable { - /// The query string + /// A raw SQL statement + /// + /// >Note: + /// Since ``PostgresNIO`` only supports the + /// [Extended Query](https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY) + /// flow, only a single sql statement is allowed. In other words: SQL statement batches will lead to server + /// errors at all times. public var sql: String - /// The query binds + + /// The parameters for the ``PostgresQuery/sql`` statement. public var binds: PostgresBindings + /// Create a ``PostgresQuery`` with a SQL statement string and bindings as ``PostgresBindings`` + /// + /// > Warning: + /// If you use string interpolation or generate the SQL statement through concatenating strings, it is your + /// responsibility to ensure that you are not prone to [SQL injection](https://en.wikipedia.org/wiki/SQL_injection) + /// attacks. + /// + /// - Parameters: + /// - sql: The SQL statement to execute. + /// - binds: The bindings for the SQL statement. public init(unsafeSQL sql: String, binds: PostgresBindings = PostgresBindings()) { self.sql = sql self.binds = binds @@ -118,6 +168,7 @@ struct PSQLExecuteStatement { var rowDescription: RowDescription? } +/// Parameters/bindings for a ``PostgresQuery/sql`` statement. public struct PostgresBindings: Sendable, Hashable { @usableFromInline struct Metadata: Sendable, Hashable { diff --git a/Tests/IntegrationTests/AsyncTests.swift b/Tests/IntegrationTests/AsyncTests.swift index ce6fe027..0ac91510 100644 --- a/Tests/IntegrationTests/AsyncTests.swift +++ b/Tests/IntegrationTests/AsyncTests.swift @@ -229,7 +229,7 @@ final class AsyncPostgresConnectionTests: XCTestCase { "foo", "default" ] - + let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } let eventLoop = eventLoopGroup.next()