PostgresClientKit

repository·master·Indexed 40 days ago

https://github.com/codewinsdotcom/postgresclientkit

A native Swift library providing an implementation of the PostgreSQL network protocol, allowing interaction with PostgreSQL databases without requiring libpq. It supports Swift 5+, PostgreSQL versions 10 through 14, and platforms including macOS, iOS, and Linux. Features include a synchronous API for connections and statements, an asynchronous ConnectionPool for high-concurrency requests, and Decodable support for mapping rows to Swift types.

Tokens
3.6K
Snippets
9
Records
17
Agent score
50%

What's inside PostgresClientKit

  1. Use connection pooling for high-concurrency requests

    master
    PostgresClientKit provides a ConnectionPool to manage a small number of shared Connection instances and threads. Use the ConnectionPool API to service a large number of requests asynchronously, which is particularly useful for server-side Swift applications.
  2. Prevent unexpected Cursor closure

    master

    In PostgresClientKit, the deinitializers for Connection, Statement, and Cursor automatically call close(). This can lead to unexpected closures if you reuse variables for subsequent SQL commands.

    The Problem: If you assign a new Statement to a variable that currently holds an old Statement, the old Statement is deinitialized. Its deinitializer calls close(), which closes any active Cursor associated with that statement. If your Cursor was only being held by that statement, it will be closed unexpectedly.

    Solutions:

    1. Explicit Close: Call .close() on the first Statement before preparing a new one.
    2. Unique Variables: Assign each Statement instance to a unique variable name.
    3. Scope Isolation: Wrap each SQL command execution in its own do { ... } block. This ensures the Statement and Cursor are deinitialized and deallocated before the next command begins.
    // Problematic pattern:
    var statement = try connection.prepareStatement(text: "SELECT 1")
    var cursor = try statement.execute()
    
    // This assignment triggers deinit of the first statement, which closes the first cursor
    statement = try connection.prepareStatement(text: "SELECT 2") 
    // cursor.isClosed will now be true
    
    // Recommended pattern (Scope Isolation):
    do {
        let statement = try connection.prepareStatement(text: "SELECT 1")
        let cursor = try statement.execute()
        // use cursor...
    }
    
    do {
        let statement = try connection.prepareStatement(text: "SELECT 2")
        let cursor = try statement.execute()
        // use cursor...
    }
  3. Explicit type conversion for column values

    master
    When retrieving values from a column, you must specify the target Swift type. This ensures that Postgres-to-Swift type conversions (such as mapping SQL NUMERIC to Int, Double, or Decimal) are explicit and robust based on your specific domain requirements.
  4. Understand the synchronous vs asynchronous API models

    master

    PostgresClientKit uses two different concurrency models depending on the object you are interacting with:

    1. Asynchronous: The ConnectionPool provides an asynchronous API designed to handle many requests efficiently using shared resources.
    2. Synchronous: The Connection, Statement, and Cursor objects provide a synchronous API. This allows you to easily sequence steps like preparing a statement, executing it, and processing returned rows in a linear flow.
  5. Prerequisites for PostgresClientKit

    master

    Before using PostgresClientKit, ensure you meet the following requirements:

    • Swift 5 or later
    • libssl-dev: Required if you are running on Linux.

    Supported Platforms & Versions:

    • OS: Linux (Ubuntu 18.04 LTS, 20.04 LTS), macOS (10.14+), iOS (12+).
    • Postgres: 10, 11, 12, 13, 14.
  6. Receive log messages from the Postgres server

    master

    To see messages sent directly from the Postgres server within your Swift application, follow these steps:

    1. Configure Postgres Server: Set the client_min_messages parameter in your postgresql.conf file (e.g., client_min_messages = debug5).
    2. Capture Logs: You can capture these messages in one of two ways:
      • Via Logger: Increase the Postgres.logger level to .finer or higher. PostgresClientKit will then log the messages received from the server.
      • Via ConnectionDelegate: Implement the ConnectionDelegate protocol and the connection(_:didReceiveNotice:) method. Assign an instance of this class to the delegate property of your Connection.

    Note: The delegate property on Connection is weak. You must maintain a strong reference to your delegate instance in your own code to prevent it from being deallocated.

    // Example: Implementing a delegate to receive notices
    class MyConnectionDelegate: ConnectionDelegate {
        func connection(_ connection: Connection, didReceiveNotice notice: Notice) {
            print("Received notice: \(notice)")
        }
    }
    
    let myDelegate = MyConnectionDelegate()
    // Ensure myDelegate is held by a strong reference elsewhere
    connection.delegate = myDelegate
  7. Configure Postgres for PostgresClientKit testing

    master

    To support the PostgresClientKit test suite, you must configure your Postgres instance with specific SSL and encryption settings.

    In your postgresql.conf file, ensure the following parameters are set:

    • ssl = on
    • password_encryption = scram-sha-256

    If the Postgres server is running on a different host than the test suite, ensure listen_addresses is set to the appropriate network interface in postgresql.conf to allow remote connections.

  8. Create the test database and users

    master

    After configuring postgresql.conf and pg_hba.conf, restart your Postgres service. Then, use the provided CreateTestEnvironment.sql script to create the postgresclientkittest database and the required test users.

    Navigate to the scripts directory and execute the script using psql with your superuser credentials.

    cd <path-to-clone>/Tests/Scripts
    psql --host=<host> --port=<port> --dbname=<dbname> --username=<superuser> < CreateTestEnvironment.sql
    
    # Example:
    psql --host=127.0.0.1 --port=5432 --dbname=postgres --username=root < CreateTestEnvironment.sql
  9. Install PostgresClientKit

    master

    You can integrate PostgresClientKit into your project using Swift Package Manager (Xcode or standalone), or CocoaPods.

    Xcode Project (Package Dependency)

    1. Select File > Add Packages...
    2. Enter the URL: https://github.com/codewinsdotcom/PostgresClientKit
    3. Set version requirements (e.g., Up To Next Major Version starting from 1.0.0).
    4. Click Add Package.

    Standalone Swift Package (Package.swift)

    Add the package to your dependencies and reference the product in your targets:

    dependencies: [
        .package(url: "https://github.com/codewinsdotcom/PostgresClientKit", from: "1.0.0"),
    ],
    targets: [
        .target(
            name: "MyProject",
            dependencies: ["PostgresClientKit"]),
    ]

    CocoaPods

    Add the following to your Podfile:

    target 'MyApp' do
      pod 'PostgresClientKit', '~> 1.0'
    end

    Then run pod install.

    After installation, import the library in your Swift files:

    import PostgresClientKit
    import PostgresClientKit