>_EXECUTIVE SUMMARY
stryke-postgres is one of the opt-in connector packages in the stryke ecosystem. PostgreSQL client for stryke. Opt-in package, kept out of the stryke core binary so the daily-driver install stays slim.
Sync postgres crate. Designed for shell pipelines + scripts, not as an ORM layer.
~ARCHITECTURE
In-process cdylib design: the stryke side is a thin .stk wrapper that calls FFI symbols on the dlopen'd library. No subprocess, no pipe — calls return on the same thread that made them.
| Layer | Implementation |
|---|---|
stryke wrappers (lib/*.stk) | Thin .stk wrapper (Postgres); exposes typed helpers that serialize args to JSON and parse the response |
cdylib (libstryke_postgres.{dylib,so}) | Single Rust cdylib crate; every public surface fn is an extern "C" fn pg__<verb>(*const c_char) -> *mut c_char |
| Process model | Library is dlopen'd once per stryke session on first use Postgres; a postgres::Client cache keyed by connection URL is held in OnceCell; lives until the runtime exits |
| Build | Cargo with [lib] crate-type = ["cdylib"]; publish = false; the package itself ships via s pkg install -g . |
| Install path | ~/.stryke/store/postgres@<version>/ after make install; use Postgres from any stryke script resolves it |
| Tests | zunit-style under t/ with live-service variants when applicable |
| CI | GitHub Actions .github/workflows/ci.yml — cargo fmt + clippy + test, plus stryke pkg install verification |
$WHY OPT-IN (NOT BUILTIN)
Sync postgres crate. Designed for shell pipelines + scripts, not as an ORM layer.
The trade-off is intentional. The core stryke binary stays under ~40 MB precisely because each connector ships separately. Daily-driver work (one-liners, awk replacement, data scripting) doesn't need MongoDB drivers, AWS SDKs, or Spark Connect bindings linked in.
&FFI ENVELOPE
JSON in / JSON out across the FFI boundary. Each Postgres::* wrapper serializes its args to a JSON dict and calls the matching pg__* symbol resolved out of libstryke_postgres.{dylib,so}; the cdylib returns a JSON CString the wrapper parses. JSON binds map to wire values via the cdylib's json_to_param (null → NULL, bool → bool, int → i64, float → f64, string → text, array/object → jsonb). The returned CString is freed via the cdylib-exported stryke_free_cstring, wired automatically by rust_ffi::load_cdylib.
# request shape (built by the .stk wrapper)
{"verb": "<op>", "...": ...}
# response shape
{"result": ...} # success — per-fn shape
{"error": "<msg>"} # failure — wrapper die()s with the message
@CONNECTION RESOLUTION
Every connecting wrapper threads its %opts through one builder (Postgres::_req in lib/Postgres.stk), which collects the connection keys and falls back to the environment. Resolution order:
| Priority | Source |
|---|---|
| 1 | url => "postgres://user:pass@host:port/db" named arg |
| 2 | individual named args: host, port, user, password, database |
| 3 | $ENV{DATABASE_URL} (or $ENV{POSTGRES_URL}) when neither url nor host is given |
Statements sharing the same resolved connection key reuse the same cached postgres::Client (connection affinity), which is what lets begin/commit, savepoints, and LISTEN/NOTIFY stay on one backend with no extra socket setup.
/PUBLIC SURFACE
The package is intentionally narrower than the underlying postgres driver — the goal is "useful from a shell pipeline", not "complete API coverage". The surface groups into the categories below; the authoritative per-function reference is the README "API reference" and the rendered Docs page.
| Category | Functions |
|---|---|
| Read paths | query, query_stream, query_one, query_col, query_scalar, dump |
| Write paths | execute, exec_file, insert_many, upsert, update, delete, truncate |
| Bulk transfer (COPY) | copy_in, copy_out |
| LISTEN / NOTIFY | notify, listen |
| Transactions | begin, commit, rollback, transaction, savepoint, release_savepoint, rollback_to |
| Metadata | ping, tables, databases, schema, count, exists, table_exists, views, functions, indexes, roles, explain, db_size, table_size, activity, locks, sequences, extensions, triggers, cancel_backend |
| Schema discovery | describe, current, schemas, matviews, server_settings, server_encoding, constraints, foreign_keys, primary_key, column_defaults, table_stats |
| Server admin / maintenance | uptime, replication, current_setting, set_config, analyze, vacuum, reindex |
| Pure helpers (no socket) | parse_dsn, parse_keyword_dsn, build_dsn, build_keyword_dsn, quote_ident, unquote_ident, quote_qualified_ident, parse_qualified_ident, quote_literal, unquote_literal, quote_nullable, escape_like, unescape_like, dollar_quote, unquote_dollar, format_array, parse_array, format_in_list, parse_in_list, parse_range, split_statements |
| Versions | version, server_version |
Injection-safety invariants hold across the surface: table, column, conflict, returning, savepoint, and maintenance-target names are identifier-validated/quoted; values are bound as positional parameters. Where a clause is interpolated rather than bound (update/delete $where, analyze/vacuum/reindex targets), the README documents it explicitly so the caller passes trusted input.
%TYPE ENCODING
Result columns are encoded Postgres → JSON before crossing the FFI boundary:
| Postgres type | JSON |
|---|---|
bool | bool |
int2, int4, int8 | number |
float4, float8 | number |
text, varchar, bpchar, name | string |
date | "YYYY-MM-DD" |
timestamp | "YYYY-MM-DD HH:MM:SS[.ffffff]" |
timestamptz | RFC 3339 |
uuid | string |
json, jsonb | preserved as JSON |
| other | string (text fallback; null when no text conversion exists) |
NULL | null |
#PROJECT METADATA
| Item | Value |
|---|---|
| License | MIT |
| Author | MenkeTechnologies |
| Repository | github.com/MenkeTechnologies/stryke-postgres |
| Parent language | strykelang |
| Meta umbrella | MenkeTechnologiesMeta |
| Issues | github.com/MenkeTechnologies/stryke-postgres/issues |