gizdb:store
the database a web app keeps its records in, one call shape over three engines. GizDB is the default: a folder, whose engine this process serves (other processes, a worker or the platform, reach it at the folder's unix socket with its password). DATABASE_URL selects another: PostgreSQL (a server; each worker process holds a pool) or a SQLite file (embedded; every worker process opens it). Apps, the feed and the model write one SQL for all (ON CONFLICT, RETURNING, $N, TRUE and FALSE); what differs, DDL types and locks, is chosen here or by the SQL Dialect (db/sql/types/dialect.giz). Typed records (db/data.giz) use the same GizDB engine through db/access.giz.
import * as Store from "gizdb:store";Types
Engine
type Engine = { endpoint: Access.Endpoint; identity: string; secret: bytes; }The GizDB engine this process serves for a folder: the endpoint typed connections reach it at from any thread (Data.of).
endpoint:M47.Endpointidentity:stringsecret:bytes
Served
type Served = { engine: Engine; server: Task.Task<Result<i64, string>>; }A GizDB engine of this process: how it is reached, and its server task.
engine:M51.Engineserver:gizlang_task.Task<_gizlang_sdk_core.Result<i64, string>>
Folder
type Folder = { pool: Pg.Pool; served: Option<Served>; }A GizDB folder open in this process: its pool, and its engine when this process serves it.
pool:gizlang_postgres.Poolserved:_gizlang_sdk_core.Option<M51.Served>
Database
type Database = | { kind: "gizdb"; folder: Ref<Folder>; } | { kind: "sqlite"; db: Sqlite.Database; } | { kind: "postgres"; pool: Pg.Pool; } | { kind: "session"; connection: Pg.Connection; }A database: a GizDB folder (its pool, and its engine when this process serves it), a SQLite file, a PostgreSQL pool, or inside a transaction the one PostgreSQL connection it runs on.
gizdbsqlitepostgressession
Rows
type Rows = { rows: Vec<Vec<Option<string>>>; affected: i64; }A statement's rows, each column as text and none for NULL, and the rows an INSERT, UPDATE or DELETE without RETURNING changed.
rows:Vec<Vec<_gizlang_sdk_core.Option<string>>>affected:i64
StoreError
type StoreError = { sqlstate: Option<string>; message: string; detail: string; position: Option<i64>; code: i64; }A store failure. PostgreSQL fills sqlstate, detail and position from
the server's error; SQLite has none of them. code is the engine's own
number (the SQLite extended result code, a transport errno), else 0.
sqlstate:_gizlang_sdk_core.Option<string>message:stringdetail:stringposition:_gizlang_sdk_core.Option<i64>code:i64
Selected
type Selected = { columns: Vec<string>; rows: Vec<Vec<Option<string>>>; }A query's result with its column names: for a reader of any result (the Giz Data console).
columns:Vec<string>rows:Vec<Vec<_gizlang_sdk_core.Option<string>>>
Moved
type Moved = { same: bool; lines: Vec<string>; }What a move found: same when every table is equal, and a line for each table.
same:boollines:Vec<string>
Functions
flag
function flag(text: string): boolA boolean column of Rows: SQLite spells true "1", PostgreSQL "t".
storeError
function storeError(message: string): StoreErrorA storage error with MESSAGE only.
integer
function integer(text: Option<string>): Result<i64, StoreError>An integer column of Rows (decimal in both engines). NULL, or text
that is not an i64, is an error, never a silent 0.
Effects: fail.
open
function open(sys: System, location: string): Result<Database, StoreError>location is postgres://USER:PASSWORD@HOST:PORT/DATABASE (no percent
escapes; as libpq, a trailing ?host=/FOLDER names the folder of the
server's unix-domain socket instead of HOST, which may then be empty), a
SQLite file (a path that ends in .db, or :memory:), else a GizDB folder
(made when missing; see gizdb). STORE_QUOTA, when set, is the most bytes
a GizDB or SQLite store holds: past it a write fails and reads go on.
A SQLite file is opened as each worker process (GIZLANG_WORKERS) needs
it: WAL, so readers never wait for the writer; a writer that finds
another worker's write in progress waits up to 5 s for it instead of
failing; commits sync at checkpoints (synchronous NORMAL: a power loss
may drop the last commits, never corrupt).
Effects: fail, read, write, console, io.
gizdbLocation
function gizdbLocation(sys: System, folder: string, password: string): stringThe postgres:// location of the GizDB server of folder (absolute) with password.
Effects: fail, io.
gizdbSocket
function gizdbSocket(sys: System, folder: string): stringThe path to listen and connect at for the socket folder GIZDB_RUN of folder (absolute).
Effects: fail, io.
socketFolder
function socketFolder(sys: System, folder: string): stringThe path of folder (absolute) for its unix-domain sockets (GIZDB_RUN, a hosted app's platform socket).
The socket files are always in the folder (a hosted app reaches them there), but a socket path may be at
most 107 bytes. For a deep folder this is /tmp/giz-HASH (the folder's SHA-256), a link to it that this makes.
Effects: fail, io.
serving
function serving(sys: System, folder: string): boolWhether a process serves the GizDB folder folder now.
Effects: fail, io.
identityColumn
function identityColumn(dialect: Dialect, name: string): stringThe DDL of column name: an i64 primary key that the database numbers upward on each INSERT and never hands out again.
Effects: fail.
move
function move(src: Database, dst: Database, verify: bool): Result<Moved, StoreError>Move the SQLite database src into dst, which has none of its tables,
and prove the copy; with verify, only compare an earlier copy.
Effects: fail, read, write, console, io.
conflictError
function conflictError(message: string): StoreErrorA failed guard (a write that had to change an exact number of rows): a serialization conflict, as the transaction's caller reads it.
stored
function stored<T>(r: Result<T, StoreError>): Result<T, string>A store result whose failure is the text of its error.
migrate
function migrate(db: Database, shape: Model.Shape): Result<Rows, StoreError>Bring the table up to the shape, additively: create it, add each missing
column (a required one with an empty, zero or false default for the rows
already there) and the indexes. A column the shape no longer has keeps its
data: dropping or converting is an explicit, separate step. One transaction
under the table's lock, so worker processes starting together migrate one
after another (PostgreSQL's CREATE TABLE IF NOT EXISTS is not safe to race).
The result's affected counts the statements that changed the schema.
Effects: fail, read, write, console, io.
migrateLocked
function migrateLocked(tx: Database, shape: Model.Shape): Result<Rows, StoreError>Within the caller's locked transaction: @was, additive columns, then indexes.
Effects: fail, read, write, console, io.
records
function records<T>(db: Database, tail: string, params: Vec<Option<string>>): Result<Vec<T>, StoreError>The records of the stored type T that tail (the SQL after "FROM TABLE", such as "WHERE org = $1 ORDER BY created")
finds with params. A Gate-checked read is Data.find; one answers a 404 when there is none.
recordsOf
function recordsOf<T>(found: Rows): Vec<T>The records T of rows whose columns are columns(shape<T>()), such as an UPDATE ... RETURNING answer.
record
function record<T>(db: Database, tail: string, params: Vec<Option<string>>): Result<Option<T>, StoreError>The first record that records finds, if any.
one
function one<T>(db: Database, tail: string, params: Vec<Option<string>>, message: string): Result<T, Model.Rejection>The record that record finds, or a 404 with message (as Data.one).
insert
function insert<T>(db: Database, shape: Model.Shape, record: T): Result<Rows, StoreError>Insert one record of a stored Shape into a SQL store (or inside its transaction). A Gate-checked write is Data.insert.
Methods
Database.dialect
function Database.dialect(self: Database): DialectThe SQL a Database speaks.
Effects: fail.
Database.query
function Database.query(self: Database, sql: string, params: Vec<Option<string>>): Result<Rows, StoreError>One statement with parameters $1.. (none for NULL).
Effects: fail, read, write, console, io.
Database.select
function Database.select(self: Database, sql: string, params: Vec<Option<string>>): Result<Selected, StoreError>One statement with parameters $1.., its rows with their column names.
Effects: fail, read, write, console, io.
Database.exec
function Database.exec(self: Database, sql: string): Result<bool, StoreError>A script of statements without parameters or results.
Effects: fail, read, write, console, io.
Database.transaction
function Database.transaction<F>(self: Database, work: F): ReturnType<F>Runs an application callback once. Commit conflicts are returned with their SQLSTATE; an application effect is never implicitly replayed.
Database.lock
function Database.lock(self: Database, name: string): Result<bool, StoreError>Inside a transaction: the lock named name until it ends, so transactions
taking the same name run one after another while readers go on reading
(the name need not be a table yet). SQLite's writers already run one after
another; PostgreSQL takes a transaction advisory lock on the name (GizDB
checks it at commit: see Database.transaction).
Effects: fail, read, write, io.
Database.columns
function Database.columns(self: Database, table: string): Result<Vec<string>, StoreError>The names of a table's columns; none when it does not exist.
Effects: fail, read, write, console, io.
Database.tables
function Database.tables(self: Database): Result<Vec<string>, StoreError>The names of the tables in the current schema, in order.
Effects: fail, read, write, console, io.
Database.engine
function Database.engine(self: Database): Option<Engine>The GizDB engine this process serves for the database, which typed records reach from any thread (db/data.giz of); none for another engine or a folder that another process serves.
Effects: fail, read.
Database.close
function Database.close(self: Database): voidClose the database (a pool waits for its lent connections to come back; the GizDB engine this process serves stops after it).
Effects: fail, read, write, io.
StoreError.conflicting
function StoreError.conflicting(self: StoreError): boolTrue when the write lost to another or broke a key or constraint: PostgreSQL class 23 (integrity) or 40 (rollback), SQLite's constraint code.
Effects: fail.
StoreError.text
function StoreError.text(self: StoreError): stringThe failure as one line: its message, then its detail when it has one.
Effects: fail.
StoreError.rejection
function StoreError.rejection(self: StoreError): Model.RejectionThe error as a rejection that a request answers: status 500 with the message.
Effects: fail.
Constants
GIZDB_FILES
const GIZDB_FILES = ".gizdb"The engine's files.
GIZDB_RUN
const GIZDB_RUN = ".gizdb.run"The folder of its one way in, the unix socket .s.PGSQL.5432 (libpq's name); no TCP port is open.
GIZDB_PASSWORD
const GIZDB_PASSWORD = ".gizdb.pw"Its password: gizdb without one would accept every client.
GIZDB_MOVED
const GIZDB_MOVED = ".gizdb.moved"The record that its first start moved the folder's SQLite file in, or found none.