postgres

PostgreSQL bindings for Kit using libpq

Files

FileDescription
.editorconfigEditor formatting configuration
.gitignoreGit ignore rules for build artifacts and dependencies
.tool-versionsasdf tool versions (Zig, Kit)
LICENSEMIT license file
README.mdThis file
examples/postgres.kitExample: postgres
kit.tomlPackage manifest with metadata and dependencies
src/postgres.kitPostgreSQL Bindings for Kit - Full-featured database access
tests/postgres.test.kitTests for postgres

Dependencies

No Kit package dependencies.

Native Dependencies

Requires the libpq client library:

PlatformInstall Command
macOSbrew install libpq
Ubuntusudo apt install libpq-dev
Fedorasudo dnf install libpq-devel
  • kit-pg-parser - PostgreSQL SQL parser - parse, validate, normalize, and fingerprint SQL queries using libpg_query
  • kit-query - Fluent SQL query builder for Kit

Installation

kit add gitlab.com/kit-lang/packages/kit-postgres.git

Usage

import Kit.Postgres as Postgres

# Connect to database (URL or parameter format)
db = Postgres.connect "postgresql://user:password@localhost:5432/mydb"
defer Postgres.close db

if Postgres.is-connected? db then
  # Query rows
  match db.query "SELECT id, name FROM users"
    | Ok rows ->
      List.each (fn(row) =>
        match Map.get "name" row
          | Some (DbString name) -> println "User: ${name}"
          | _ -> println "NULL"
      ) rows
    | Err e -> println "Query error: ${e}"

  # Parameterized query (SQL injection safe)
  match db.query-params "SELECT * FROM users WHERE name = $1" ["Alice"]
    | Ok rows -> println "Found ${List.length rows} users"
    | Err e -> println "Error: ${e}"

  # Execute statements (INSERT, UPDATE, DELETE)
  match db.execute "INSERT INTO users (name) VALUES ('Bob')"
    | Ok _ -> println "Inserted"
    | Err e -> println "Error: ${e}"

  # Parameterized execute
  match db.execute-params "UPDATE users SET name = $1 WHERE id = $2" ["Charlie", "1"]
    | Ok _ -> println "Updated"
    | Err e -> println "Error: ${e}"

  # Escape string for safe SQL inclusion
  escaped = db.escape "O'Brien"
  println "Escaped: ${escaped}"
else
  println "Connection failed"

Development

Running Examples

Run examples with the interpreter:

kit run examples/postgres.kit

Compile examples to a native binary:

kit build examples/postgres.kit -o postgres && ./postgres

Running Tests

Run the test suite:

kit test

Run the test suite with coverage:

kit test --coverage

Running kit dev

Run the standard development workflow (format, check, test):

kit dev

This will:

  1. Format and check source files in src/
  2. Run tests in tests/ with coverage

Generating Documentation

Generate API documentation from doc comments:

kit doc

Note: Kit sources with doc comments (##) will generate HTML documents in docs/*.html

Cleaning Build Artifacts

Remove generated files, caches, and build artifacts:

kit task clean

Note: Defined in kit.toml.

Local Installation

To install this package locally for development:

kit install

This installs the package to ~/.kit/packages/@kit/postgres/, making it available for import as Kit.Postgres in other projects.

License

This package is released under the MIT License - see LICENSE for details.

Exported Functions & Types

connection-ok

Connection established successfully

connection-bad

Connection failed or was terminated

pgres-empty-query

Query returned no results (empty query string)

pgres-command-ok

Command completed successfully without returning rows (INSERT, UPDATE, DELETE, etc.)

pgres-tuples-ok

Query completed successfully and returned rows

pgres-copy-out

Server is ready to send COPY data to client

pgres-copy-in

Server is ready to receive COPY data from client

pgres-bad-response

Server sent a response the client could not understand

pgres-nonfatal-error

Non-fatal error occurred (notice or warning)

pgres-fatal-error

Fatal error occurred during query execution

PostgresError

PostgreSQL error type for typed error handling. Variants distinguish between different failure modes.

Variants

PostgresConnectionError {message}
PostgresQueryError {message}
PostgresExecError {message}

DbValue

Database value type representing PostgreSQL column values. Row records contain Option DbValue for each column: - None: SQL NULL - Some (DbString s): Scalar string value - Some (DbArray items): Array of strings

match Map.get "tags" row
  | Some (DbArray tags) -> List.each println tags
  | Some (DbString s) -> println s
  | None -> println "NULL"

Variants

DbString {String}
DbArray {_0}

oid-bool

Boolean type

oid-int2

Small integer type (int2, 2 bytes)

oid-int4

Integer type (int4, 4 bytes)

oid-int8

Big integer type (int8, 8 bytes)

oid-float4

Single precision floating point (float4, 4 bytes)

oid-float8

Double precision floating point (float8, 8 bytes)

oid-numeric

Arbitrary precision numeric type

oid-text

Variable-length text type

oid-varchar

Variable-length character type with limit

oid-char

Single character type

oid-bpchar

Blank-padded fixed-length character type

oid-name

Internal name type (63-byte fixed length)

oid-date

Calendar date type (year, month, day)

oid-time

Time of day type (no time zone)

oid-timestamp

Date and time type (no time zone)

oid-timestamptz

Date and time type with time zone

oid-interval

Time interval type

oid-uuid

Universally unique identifier type

oid-json

JSON text type

oid-jsonb

Binary JSON type (decomposed, indexed)

oid-bytea

Binary data type (byte array)

oid-bool-array

Boolean array type

oid-int2-array

Small integer array type

oid-int4-array

Integer array type

oid-int8-array

Big integer array type

oid-float4-array

Single precision float array type

oid-float8-array

Double precision float array type

oid-numeric-array

Numeric array type

oid-text-array

Text array type

oid-varchar-array

Varchar array type

oid-char-array

Char array type

oid-bpchar-array

Blank-padded char array type

oid-name-array

Name array type

oid-date-array

Date array type

oid-time-array

Time array type

oid-timestamp-array

Timestamp array type

oid-timestamptz-array

Timestamp with time zone array type

oid-uuid-array

UUID array type

oid-json-array

JSON array type

oid-jsonb-array

Binary JSON array type

connect

Note: This module uses Kit's standard Result type (Ok a | Err PostgresError).

Connect to PostgreSQL database and return a connection object.

Parameters:

Connection string format: "host=localhost port=5432 dbname=mydb user=myuser password=secret"

Database URL format: "postgresql://user:password@localhost:5432/mydb" "postgres://user:password@localhost/mydb"

Returns: - handle: Ptr - Internal libpq connection handle- driver: String - "postgres"- connected: Bool - Connection status- error: String - Error message if connection failed- query: Function - Execute query and return rows- execute: Function - Execute statement- escape: Function - Escape string for safe SQL inclusion- query-params: Function - Execute query with $1, $2 parameters- execute-params: Function - Execute statement with $1, $2 parameters

Note: Returns connection object even if connection fails. Check with is-connected?

String -> {handle: Ptr, driver: String, connected: Bool, error: String, query: String -> Result [Map String (Option DbValue)] PostgresError, execute: String -> Result Int PostgresError, escape: String -> String, query-params: String -> [String] -> Result [Map String (Option DbValue)] PostgresError, execute-params: String -> [String] -> Result Int PostgresError}

db = Postgres.connect "postgresql://localhost/mydb"
if Postgres.is-connected? db then
  result = db.query "SELECT * FROM users"
  # Parameterized query (SQL injection safe)
  result = db.query-params "SELECT * FROM users WHERE name = $1" ["Alice"]

try-connect

Try to connect to PostgreSQL database with Result type error handling.

Parameters:

Returns:

Note: Unlike connect, this immediately closes connection on failure.

String -> Result {handle: Ptr, driver: String, connected: Bool, error: String, query: String -> Result [Map String (Option DbValue)] PostgresError, execute: String -> Result Int PostgresError, escape: String -> String, query-params: String -> [String] -> Result [Map String (Option DbValue)] PostgresError, execute-params: String -> [String] -> Result Int PostgresError} PostgresError

match Postgres.try-connect "postgresql://user:pass@localhost/mydb"
  | Ok db ->
      result = db.query "SELECT * FROM users"
      Postgres.close db
  | Err e ->
      print "Connection failed: ${PostgresError.message e}"

is-connected?

Check if a database connection is currently valid and active.

Parameters:

Returns:

{connected: Bool} -> Bool

if Postgres.is-connected? db then
  # Perform queries

close

Close a database connection and release associated resources.

Parameters:

Returns:

Note: Always close connections when done to free resources.

{handle: Ptr} -> Unit

db = Postgres.connect "postgresql://localhost/mydb"
# ... use db ...
Postgres.close db

query

Execute a query and return all rows as a list of records.

Parameters:

Returns:

Row format: - Each row is a Record (map) with column names as keys - Values are Option DbValue: - None: SQL NULL - Some (DbString s): Scalar value as string - Some (DbArray items): PostgreSQL array as List String

Array columns are automatically parsed from PostgreSQL text format.

Ptr -> NonEmptyString -> Result [Map String (Option DbValue)] PostgresError

match Map.get "name" row
  | Some (DbString name) -> println "Name: ${name}"
  | _ -> println "No name"

match Map.get "tags" row
  | Some (DbArray tags) -> List.each println tags
  | _ -> println "No tags"

match query handle "SELECT id, name FROM users"
  | Ok rows ->
      List.each (fn(row) =>
        match Map.get "name" row
          | Some (Some name) -> print name
          | Some None -> print "NULL"
          | None -> print "Column not found"
      ) rows
  | Err e -> print "Error: ${PostgresError.show e}"

execute

Execute a SQL statement (INSERT, UPDATE, DELETE, CREATE, etc.).

Parameters:

Returns:

Note: Use for statements that don't return rows (INSERT, UPDATE, DELETE, CREATE, DROP, etc.)

Ptr -> NonEmptyString -> Result Int PostgresError

match execute handle "INSERT INTO users (name) VALUES ('Alice')"
  | Ok _ -> print "Statement executed"
  | Err e -> print "Error: ${PostgresError.show e}"

escape

Escape a string value for safe inclusion in SQL queries.

Parameters:

Returns:

Note: Uses libpq's PQescapeLiteral for proper escaping with correct memory management. The returned string includes surrounding quotes, so use it directly: "WHERE name = ${escaped}" NOT "WHERE name = '${escaped}'"

Ptr -> String -> String

escaped = escape handle "O'Brien"
# => "'O''Brien'"
query-str = "SELECT * FROM users WHERE name = ${escaped}"

query-params

Execute a query with parameterized values for SQL injection protection.

Parameters:

Returns:

Note: Uses native PQexecParams for proper parameterized queries. Parameters are 1-indexed ($1 is first parameter).

Ptr -> NonEmptyString -> [String] -> Result [Map String (Option DbValue)] PostgresError

match query-params handle "SELECT * FROM users WHERE name = $1 AND status = $2" ["Alice", "active"]
  | Ok rows -> process rows
  | Err e -> print "Error: ${PostgresError.show e}"

execute-params

Execute a statement with parameterized values for SQL injection protection.

Parameters:

Returns:

Ptr -> NonEmptyString -> [String] -> Result Int PostgresError

match execute-params handle "INSERT INTO users (name, email) VALUES ($1, $2)" ["Alice", "alice@example.com"]
  | Ok _ -> print "Inserted"
  | Err e -> print "Error: ${PostgresError.show e}"

parse-array

Parse a PostgreSQL text-format array string into a list of strings.

Handles the PostgreSQL array text representation format: - Empty arrays: {} -> [] - Simple values: {a,b,c} -> ["a", "b", "c"] - Quoted values: {"hello world","with,comma"} -> ["hello world", "with,comma"] - Escaped quotes: {"say \"hello\""} -> ["say \"hello\""] - Escaped backslashes: {"back\\slash"} -> ["back\slash"] - NULL values: {a,NULL,b} -> ["a", None, "b"] (represented as empty string)

Note: This handles 1-dimensional arrays. Nested arrays are returned as raw strings.

String -> [String]