DrinkMeAlice (drink_me_alice v0.1.0)

Copy Markdown

A bounded SurrealDB client for Elixir, qualified against SurrealDB 3.0.5 (see the README's Compatibility section for the version policy and the cleartext-transport deployment boundary).

Layers, bottom up:

This module holds the read-side helper query/4 and the shared identifier check.

Summary

Functions

SurrealDB 3.0.5 protects the parameter names $access, $auth, $session, $token: binding one makes the server refuse the WHOLE request (JSON-RPC -32600, measured). :ok, or {:error, {:protected_param, name}} before sending. Only atom and binary key names are inspected; other key types are rejected later by the outbound encoder with its own typed error, so this check never raises on them.

Wire value for an EXACT string parameter — pair it with exact_string_sql/1.

The SurrealQL expression that recovers a list of exact_string/1 values in param name.

The SurrealQL expression that recovers a exact_string/1 param named name.

True for a SurrealQL identifier this connector will splice into SQL: [A-Za-z_][A-Za-z0-9_]*, at most 64 bytes. Everything else travels as a bound $param.

Run one read-side query through a Conn owner.

Normalize a query's parameter map: atom names become strings (the outbound encoder (DrinkMeAlice.Json.encode/1) accepts only string keys). Values must already be canonical-domain terms — binaries, i64 integers, booleans, nil, lists and string-keyed maps; floats, decimals and datetimes travel as strings with a SurrealQL cast.

Functions

check_param_names(vars)

@spec check_param_names(map()) :: :ok | {:error, {:protected_param, binary()}}

SurrealDB 3.0.5 protects the parameter names $access, $auth, $session, $token: binding one makes the server refuse the WHOLE request (JSON-RPC -32600, measured). :ok, or {:error, {:protected_param, name}} before sending. Only atom and binary key names are inspected; other key types are rejected later by the outbound encoder with its own typed error, so this check never raises on them.

exact_string(value)

@spec exact_string(binary()) :: binary()

Wire value for an EXACT string parameter — pair it with exact_string_sql/1.

SurrealDB 3.0.5's JSON-RPC parameter conversion coerces strings that merely look typed — at any depth, inside arrays and objects too:

sentarrives as<string> of it
"2026-10-01"datetime"2026-10-01T00:00:00Z" ✗
"task:abc"record id"task:abc"
UUID-shaped stringuuidthe same string

A <string>$p cast repairs the UUID and record-id cases but NOT date-shaped strings, which come back rewritten. Prefixing one character that no coercion recognises and slicing it off server-side is exact for every string (verified against the pinned server, including the empty string and non-ASCII text).

exact_string_list_sql(name)

@spec exact_string_list_sql(binary()) :: binary()

The SurrealQL expression that recovers a list of exact_string/1 values in param name.

exact_string_sql(name)

@spec exact_string_sql(binary()) :: binary()

The SurrealQL expression that recovers a exact_string/1 param named name.

identifier?(s)

@spec identifier?(term()) :: boolean()

True for a SurrealQL identifier this connector will splice into SQL: [A-Za-z_][A-Za-z0-9_]*, at most 64 bytes. Everything else travels as a bound $param.

query(conn, sql, vars \\ %{}, opts \\ [])

@spec query(GenServer.server(), binary(), map(), keyword()) ::
  {:ok, list()} | {:error, term()}

Run one read-side query through a Conn owner.

Returns {:ok, results} where results is the per-statement array (%{"status" => "OK" | "ERR", "result" => _} each — callers check every status), or {:error, reason}:

  • {:rpc_error, code} / :session_refused — the server refused;
  • {:lost, reason} — sent, no reply (for a write this is an UNKNOWN outcome, never a failure — use DrinkMeAlice.Tx.run/4 for writes);
  • {:refused, reason} — certified never sent (:down, :expired, :exhausted, :too_large, :encode).

Options: deadline_ms (≤ 2_000), class (:general | :internal).

stringify_param_names(vars)

@spec stringify_param_names(map()) :: map()

Normalize a query's parameter map: atom names become strings (the outbound encoder (DrinkMeAlice.Json.encode/1) accepts only string keys). Values must already be canonical-domain terms — binaries, i64 integers, booleans, nil, lists and string-keyed maps; floats, decimals and datetimes travel as strings with a SurrealQL cast.