Runlight.Db behaviour (runlight v0.1.0)

Copy Markdown View Source

The little the store needs from a database: its dialect, and statements with ? placeholders that answer rows. Runlight.Db.Ecto runs them through an app's own Ecto repo; anything else that implements the callbacks below can stand in.

SQL is written with ? placeholders and "double quotes" around a name that is a keyword somewhere. Each implementation turns that into what its database reads, as the SDK's drivers do: Postgres gets the values written into the statement as text, as node-postgres sends them, and MySQL gets them escaped in place with backticks for quoted names, as mysql2 does.

Summary

Callbacks

How many rows an UPDATE or DELETE matched.

Rows as JavaScript objects, their keys in column order.

Runs fun while holding a database-wide lock, so two processes starting at once do not race to create tables.

Runs fun in one transaction on one connection, committing what it did, or rolling back when it raises.

Functions

The dialect's name, as the SDK writes it.

SQL written for SQLite and Postgres, as MySQL and MariaDB read it (the SDK's mysqlText): ? becomes the value, escaped; a "quoted" identifier is quoted with backticks; and a backslash inside 'text' is doubled, since MySQL reads it as an escape where standard SQL takes it literally.

? placeholders to $1, $2, ..., leaving quoted text alone (the Postgres driver's numberPlaceholders).

The statement with each ? replaced by its value written as Postgres text: a quoted literal of unknown type, as node-postgres sends every value, so the database reads it in the type the statement needs.

Types

dialect()

@type dialect() :: :sqlite | :postgres | :mysql

t()

@type t() :: %Runlight.Db{
  dialect: dialect(),
  metered: boolean(),
  module: module(),
  state: term()
}

Callbacks

affected(t, t, list)

@callback affected(t(), String.t(), [term()]) :: non_neg_integer()

How many rows an UPDATE or DELETE matched.

all(t, t, list)

@callback all(t(), String.t(), [term()]) :: [Runlight.JS.Object.t()]

Rows as JavaScript objects, their keys in column order.

exclusive(t, function)

@callback exclusive(t(), (t() -> term())) :: term()

Runs fun while holding a database-wide lock, so two processes starting at once do not race to create tables.

run(t, t, list)

@callback run(t(), String.t(), [term()]) :: :ok

transaction(t, function)

@callback transaction(t(), (t() -> term())) :: term()

Runs fun in one transaction on one connection, committing what it did, or rolling back when it raises.

Functions

affected(db, sql, params \\ [])

@spec affected(t(), String.t(), [term()]) :: non_neg_integer()

all(db, sql, params \\ [])

@spec all(t(), String.t(), [term()]) :: [Runlight.JS.Object.t()]

dialect(db)

@spec dialect(t()) :: String.t()

The dialect's name, as the SDK writes it.

exclusive(db, fun)

@spec exclusive(t(), (t() -> term())) :: term()

mysql_text(sql, params)

@spec mysql_text(String.t(), [term()]) :: String.t()

SQL written for SQLite and Postgres, as MySQL and MariaDB read it (the SDK's mysqlText): ? becomes the value, escaped; a "quoted" identifier is quoted with backticks; and a backslash inside 'text' is doubled, since MySQL reads it as an escape where standard SQL takes it literally.

number_placeholders(sql)

@spec number_placeholders(String.t()) :: String.t()

? placeholders to $1, $2, ..., leaving quoted text alone (the Postgres driver's numberPlaceholders).

postgres_text(sql, params)

@spec postgres_text(String.t(), [term()]) :: String.t()

The statement with each ? replaced by its value written as Postgres text: a quoted literal of unknown type, as node-postgres sends every value, so the database reads it in the type the statement needs.

run(db, sql, params \\ [])

@spec run(t(), String.t(), [term()]) :: :ok

transaction(db, fun)

@spec transaction(t(), (t() -> term())) :: term()