// STRYKE-POSTGRES — ENGINEERING REPORT

Opt-in stryke connector package · cdylib libstryke_postgres.{dylib,so} (crate-type = cdylib) · dlopen'd in-process on use Postgres · JSON-over-FFI envelope

>_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.

cdylib
Crate type
opt-in
Tier
dlopen
Load model
JSON-over-FFI
Envelope

~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.

LayerImplementation
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 modelLibrary 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
BuildCargo 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
Testszunit-style under t/ with live-service variants when applicable
CIGitHub 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:

PrioritySource
1url => "postgres://user:pass@host:port/db" named arg
2individual 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.

CategoryFunctions
Read pathsquery, query_stream, query_one, query_col, query_scalar, dump
Write pathsexecute, exec_file, insert_many, upsert, update, delete, truncate
Bulk transfer (COPY)copy_in, copy_out
LISTEN / NOTIFYnotify, listen
Transactionsbegin, commit, rollback, transaction, savepoint, release_savepoint, rollback_to
Metadataping, tables, databases, schema, count, exists, table_exists, views, functions, indexes, roles, explain, db_size, table_size, activity, locks, sequences, extensions, triggers, cancel_backend
Schema discoverydescribe, current, schemas, matviews, server_settings, server_encoding, constraints, foreign_keys, primary_key, column_defaults, table_stats
Server admin / maintenanceuptime, 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
Versionsversion, 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 typeJSON
boolbool
int2, int4, int8number
float4, float8number
text, varchar, bpchar, namestring
date"YYYY-MM-DD"
timestamp"YYYY-MM-DD HH:MM:SS[.ffffff]"
timestamptzRFC 3339
uuidstring
json, jsonbpreserved as JSON
otherstring (text fallback; null when no text conversion exists)
NULLnull

#PROJECT METADATA

ItemValue
LicenseMIT
AuthorMenkeTechnologies
Repositorygithub.com/MenkeTechnologies/stryke-postgres
Parent languagestrykelang
Meta umbrellaMenkeTechnologiesMeta
Issuesgithub.com/MenkeTechnologies/stryke-postgres/issues