sqlode/runtime

Types

Why expand_slice_placeholders_checked rejected its input.

  • SliceLengthNegative covers a slices entry whose length is negative. Lengths must be >= 0 (zero is permitted; the placeholder list collapses to NULL per the SQL IN () rewrite convention).
  • SliceIndexOutOfRange covers a slices entry whose 1-based index falls outside [1, total_params]. Indices outside that window can never match the loop’s orig_idx and are silently ignored otherwise.
  • TotalParamsNegative covers a total_params that is below 0. Parameter counts cannot be negative; without this check the int.range loop in expand_slice_placeholders would call param_marker(0), which panics post-#551. (#565)
pub type ExpandError {
  SliceLengthNegative(index: Int, length: Int)
  SliceIndexOutOfRange(index: Int, total_params: Int)
  TotalParamsNegative(total_params: Int)
}

Constructors

  • SliceLengthNegative(index: Int, length: Int)
  • SliceIndexOutOfRange(index: Int, total_params: Int)
  • TotalParamsNegative(total_params: Int)

Why param_marker_checked / slice_marker_checked rejected its input.

  • MarkerIndexNonPositive — the supplied index is <= 0. Marker indices are 1-based; values below 1 would either fail to round-trip through expand_slice_placeholders (leaving the literal marker in the SQL → runtime SQL syntax error) or silently match an unrelated marker (silent SQL-shape bug). Same precondition as the panicking param_marker/1 / slice_marker/1 constructors.
pub type MarkerError {
  MarkerIndexNonPositive(index: Int)
}

Constructors

  • MarkerIndexNonPositive(index: Int)

Placeholder style used by the target database driver.

SQLite accepts both ?1-style and bare-?-style placeholders, so the engine column below marks both rows ✓ for sqlite. Pick the variant whose syntax matches the query strings your codegen — or hand-rolled SQL — actually emits.

VariantSyntaxPostgreSQLMySQLSQLite
DollarNumbered$1, $2, …
QuestionNumbered?1, ?2, …
QuestionPositionalbare ?
pub type PlaceholderStyle {
  DollarNumbered
  QuestionNumbered
  QuestionPositional
}

Constructors

  • DollarNumbered

    PostgreSQL: $1, $2, … Indices are 1-based and explicit.

  • QuestionNumbered

    SQLite numbered form: ?1, ?2, … Indices are 1-based and explicit. PostgreSQL and MySQL do not accept this syntax.

  • QuestionPositional

    Bare ? matched by position. Used by both MySQL and SQLite. PostgreSQL does not accept this syntax.

pub type QueryCommand {
  One
  Many
  Exec
  ExecResult
  ExecRows
  ExecLastId
  BatchOne
  BatchMany
  BatchExec
  CopyFrom
}

Constructors

  • One
  • Many
  • Exec
  • ExecResult
  • ExecRows
  • ExecLastId
  • BatchOne
  • BatchMany
  • BatchExec
  • CopyFrom

Lightweight metadata for a query, used by the generated all() function to list every query in a module without carrying encoder closures.

pub type QueryInfo {
  QueryInfo(
    name: String,
    sql: String,
    command: QueryCommand,
    param_count: Int,
  )
}

Constructors

  • QueryInfo(
      name: String,
      sql: String,
      command: QueryCommand,
      param_count: Int,
    )

A typed raw query descriptor that bundles SQL metadata with its parameter encoder. The type parameter p represents the parameter type for this query, which ties the query to its expected parameters at the type level.

placeholder_style records the dialect the generator emitted the SQL for, so callers of prepare do not need to know or repeat the engine choice: they pass the query and parameters and get back a final SQL string already rendered for the target driver.

pub type RawQuery(p) {
  RawQuery(
    name: String,
    sql: String,
    command: QueryCommand,
    param_count: Int,
    placeholder_style: PlaceholderStyle,
    encode: fn(p) -> List(Value),
    slice_info: fn(p) -> List(#(Int, Int)),
  )
}

Constructors

  • RawQuery(
      name: String,
      sql: String,
      command: QueryCommand,
      param_count: Int,
      placeholder_style: PlaceholderStyle,
      encode: fn(p) -> List(Value),
      slice_info: fn(p) -> List(#(Int, Int)),
    )
pub type Value {
  Null
  String(String)
  Int(Int)
  Float(Float)
  Bool(Bool)
  Bytes(BitArray)
  Array(List(Value))
}

Constructors

  • Null
  • String(String)
  • Int(Int)
  • Float(Float)
  • Bool(Bool)
  • Bytes(BitArray)
  • Array(List(Value))

Values

pub fn array(values: List(Value)) -> Value
pub fn bool(value: Bool) -> Value
pub fn bytes(value: BitArray) -> Value
pub fn expand_slice_placeholders(
  sql: String,
  slices: List(#(Int, Int)),
  total_params: Int,
  style: PlaceholderStyle,
) -> String

Render a parameter marker into the final engine-specific placeholder.

The generator emits __sqlode_param_N__ / __sqlode_slice_N__ in the SQL template regardless of the target engine. At runtime this function replaces each marker with the correct placeholder string (for example $3 for PostgreSQL, ?3 for SQLite, ? for MySQL) and expands slice markers to a comma-separated list sized by the caller-provided slices. Non-slice markers are renumbered sequentially across the whole SQL text so that slices that precede them shift their index.

Using markers instead of rewriting prefix<>index directly means placeholder-like text inside string literals or comments is never touched, and MySQL (which uses bare ? rather than ?N) works without special-casing the placeholder format.

pub fn expand_slice_placeholders_checked(
  sql: String,
  slices: List(#(Int, Int)),
  total_params: Int,
  style: PlaceholderStyle,
) -> Result(String, ExpandError)

Like expand_slice_placeholders, but returns the validation failure as a Result instead of panicking. Use this when slices or total_params come from a custom adapter / hand-rolled RawQuery and the caller wants to surface bookkeeping mistakes without crashing the process.

On success the returned string is identical to expand_slice_placeholders(sql, slices, total_params, style).

pub fn float(value: Float) -> Value
pub fn int(value: Int) -> Value
pub fn null() -> Value
pub fn nullable(
  value: option.Option(a),
  encode: fn(a) -> Value,
) -> Value
pub fn param_marker(index: Int) -> String

Marker prefix emitted by the generator for a regular sqlode.arg / sqlode.narg / @name parameter at the given 1-based index. Rendered into the final placeholder at runtime by expand_slice_placeholders.

Panics with "sqlode.runtime.param_marker: index must be >= 1 (1-based) (got <n>)" when index < 1. The runtime expand step is keyed by 1-based positions, so a 0 or negative index would either round-trip to a syntactically broken SQL string or silently match an unrelated marker — both constitute SQL-shape bugs. Use param_marker_checked/1 instead when the caller wants to surface the precondition as a Result (for example, in a custom adapter that accepts user-supplied indices).

pub fn param_marker_checked(
  index: Int,
) -> Result(String, MarkerError)

Like param_marker/1, but returns the precondition failure as a Result instead of panicking. Use this when index comes from a custom adapter or hand-rolled RawQuery and the caller wants to surface bookkeeping mistakes without crashing the process. Same reasoning as expand_slice_placeholders_checked (#546).

On success the returned string is identical to param_marker(index).

pub fn prepare(
  query: RawQuery(p),
  params: p,
) -> #(String, List(Value))

Prepare a raw query for execution by encoding parameters and expanding the engine-agnostic placeholder markers that the generator emits. The target placeholder dialect is read from query.placeholder_style, so callers no longer need to pass a separate style argument. Returns the final SQL string and the flattened parameter values, ready to be passed to a database driver.

pub fn raw_query(
  name name: String,
  sql sql: String,
  command command: QueryCommand,
  param_count param_count: Int,
  placeholder_style placeholder_style: PlaceholderStyle,
  encode encode: fn(p) -> List(Value),
  slice_info slice_info: fn(p) -> List(#(Int, Int)),
) -> RawQuery(p)

Construct a RawQuery directly. Behaviour is identical to invoking the RawQuery(...) constructor; the named helper exists so the intent (“this is a hand-rolled RawQuery, not codegen output”) is obvious at the call site and discoverable via the docs.

Use this helper when:

  • using sqlode/runtime as a library, bypassing sqlode generate codegen for hand-rolled queries that live next to your handler code;
  • writing a custom adapter (in-memory test database, SQLite WASM shim, query-log middleware, …) that needs to exercise prepare(query, params) against a hand-rolled RawQuery without running the codegen pipeline; or
  • writing property / regression tests for the runtime surface (prepare, expand_slice_placeholders) without regenerating fixtures every time.

Production callers who run codegen should keep using the RawQuery values sqlode generate produces from declarative SQL fixtures — they pin the SQL string and parameter shape next to the schema, which this helper does not.

pub fn raw_query_for_test(
  name name: String,
  sql sql: String,
  command command: QueryCommand,
  param_count param_count: Int,
  placeholder_style placeholder_style: PlaceholderStyle,
  encode encode: fn(p) -> List(Value),
  slice_info slice_info: fn(p) -> List(#(Int, Int)),
) -> RawQuery(p)

Deprecated: Use `runtime.raw_query` instead. The `_for_test` suffix discouraged library-mode callers even though library use is exactly what the helper was built for.

Deprecated alias for raw_query/7. The _for_test suffix discouraged library-mode callers from using the only sanctioned non-constructor path even though library use is exactly what the helper was built for. raw_query/7 carries the same behaviour without the suffix.

pub fn slice_marker(index: Int) -> String

Marker prefix emitted by the generator for a sqlode.slice parameter at the given 1-based index. Rendered into the expanded placeholder list at runtime by expand_slice_placeholders.

Panics with "sqlode.runtime.slice_marker: index must be >= 1 (1-based) (got <n>)" when index < 1. Same reasoning as param_marker/1. Use slice_marker_checked/1 instead when the caller wants to surface the precondition as a Result.

pub fn slice_marker_checked(
  index: Int,
) -> Result(String, MarkerError)

Like slice_marker/1, but returns the precondition failure as a Result instead of panicking. Same shape and reasoning as param_marker_checked/1.

pub fn string(value: String) -> Value
Search Document