Current section

Files

Jump to
sqlode src sqlode@runtime.erl
Raw

src/sqlode@runtime.erl

-module(sqlode@runtime).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/sqlode/runtime.gleam").
-export([null/0, string/1, int/1, float/1, bool/1, bytes/1, array/1, nullable/2, param_marker/1, slice_marker/1, expand_slice_placeholders/4, prepare/2]).
-export_type([query_command/0, value/0, query_info/0, placeholder_style/0, raw_query/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type query_command() :: query_one |
query_many |
query_exec |
query_exec_result |
query_exec_rows |
query_exec_last_id |
query_batch_one |
query_batch_many |
query_batch_exec |
query_copy_from.
-type value() :: sql_null |
{sql_string, binary()} |
{sql_int, integer()} |
{sql_float, float()} |
{sql_bool, boolean()} |
{sql_bytes, bitstring()} |
{sql_array, list(value())}.
-type query_info() :: {query_info,
binary(),
binary(),
query_command(),
integer()}.
-type placeholder_style() :: dollar_numbered |
question_numbered |
question_positional.
-type raw_query(HCF) :: {raw_query,
binary(),
binary(),
query_command(),
integer(),
placeholder_style(),
fun((HCF) -> list(value())),
fun((HCF) -> list({integer(), integer()}))}.
-file("src/sqlode/runtime.gleam", 85).
-spec null() -> value().
null() ->
sql_null.
-file("src/sqlode/runtime.gleam", 89).
-spec string(binary()) -> value().
string(Value) ->
{sql_string, Value}.
-file("src/sqlode/runtime.gleam", 93).
-spec int(integer()) -> value().
int(Value) ->
{sql_int, Value}.
-file("src/sqlode/runtime.gleam", 97).
-spec float(float()) -> value().
float(Value) ->
{sql_float, Value}.
-file("src/sqlode/runtime.gleam", 101).
-spec bool(boolean()) -> value().
bool(Value) ->
{sql_bool, Value}.
-file("src/sqlode/runtime.gleam", 105).
-spec bytes(bitstring()) -> value().
bytes(Value) ->
{sql_bytes, Value}.
-file("src/sqlode/runtime.gleam", 109).
-spec array(list(value())) -> value().
array(Values) ->
{sql_array, Values}.
-file("src/sqlode/runtime.gleam", 113).
-spec nullable(gleam@option:option(HCK), fun((HCK) -> value())) -> value().
nullable(Value, Encode) ->
case Value of
{some, V} ->
Encode(V);
none ->
sql_null
end.
-file("src/sqlode/runtime.gleam", 123).
?DOC(
" Marker prefix emitted by the generator for a regular `sqlode.arg` /\n"
" `sqlode.narg` / `@name` parameter at the given 1-based index.\n"
" Rendered into the final placeholder at runtime by `expand_slice_placeholders`.\n"
).
-spec param_marker(integer()) -> binary().
param_marker(Index) ->
<<<<"__sqlode_param_"/utf8, (erlang:integer_to_binary(Index))/binary>>/binary,
"__"/utf8>>.
-file("src/sqlode/runtime.gleam", 130).
?DOC(
" Marker prefix emitted by the generator for a `sqlode.slice` parameter at\n"
" the given 1-based index. Rendered into the expanded placeholder list at\n"
" runtime by `expand_slice_placeholders`.\n"
).
-spec slice_marker(integer()) -> binary().
slice_marker(Index) ->
<<<<"__sqlode_slice_"/utf8, (erlang:integer_to_binary(Index))/binary>>/binary,
"__"/utf8>>.
-file("src/sqlode/runtime.gleam", 201).
-spec render_placeholder(placeholder_style(), integer()) -> binary().
render_placeholder(Style, Index) ->
case Style of
dollar_numbered ->
<<"$"/utf8, (erlang:integer_to_binary(Index))/binary>>;
question_numbered ->
<<"?"/utf8, (erlang:integer_to_binary(Index))/binary>>;
question_positional ->
<<"?"/utf8>>
end.
-file("src/sqlode/runtime.gleam", 148).
?DOC(
" Render a parameter marker into the final engine-specific placeholder.\n"
"\n"
" The generator emits `__sqlode_param_N__` / `__sqlode_slice_N__` in the SQL\n"
" template regardless of the target engine. At runtime this function\n"
" replaces each marker with the correct placeholder string (for example\n"
" `$3` for PostgreSQL, `?3` for SQLite, `?` for MySQL) and expands slice\n"
" markers to a comma-separated list sized by the caller-provided\n"
" `slices`. Non-slice markers are renumbered sequentially across the\n"
" whole SQL text so that slices that precede them shift their index.\n"
"\n"
" Using markers instead of rewriting `prefix<>index` directly means\n"
" placeholder-like text inside string literals or comments is never\n"
" touched, and MySQL (which uses bare `?` rather than `?N`) works\n"
" without special-casing the placeholder format.\n"
).
-spec expand_slice_placeholders(
binary(),
list({integer(), integer()}),
integer(),
placeholder_style()
) -> binary().
expand_slice_placeholders(Sql, Slices, Total_params, Style) ->
{_, Mapping} = gleam@int:range(
1,
Total_params + 1,
{1, []},
fun(Acc, Orig_idx) ->
{Next_new_idx, Map} = Acc,
case gleam@list:find(
Slices,
fun(S) -> erlang:element(1, S) =:= Orig_idx end
) of
{ok, {_, Len}} ->
Marker = slice_marker(Orig_idx),
case Len of
0 ->
{Next_new_idx, [{Marker, <<"NULL"/utf8>>} | Map]};
_ ->
Expanded = begin
_pipe = gleam@int:range(
Next_new_idx,
Next_new_idx + Len,
[],
fun(Items, I) ->
[render_placeholder(Style, I) | Items]
end
),
_pipe@1 = lists:reverse(_pipe),
gleam@string:join(_pipe@1, <<", "/utf8>>)
end,
{Next_new_idx + Len, [{Marker, Expanded} | Map]}
end;
{error, _} ->
Marker@1 = param_marker(Orig_idx),
{Next_new_idx + 1,
[{Marker@1, render_placeholder(Style, Next_new_idx)} |
Map]}
end
end
),
_pipe@2 = Mapping,
_pipe@3 = lists:reverse(_pipe@2),
gleam@list:fold(
_pipe@3,
Sql,
fun(S@1, Entry) ->
{Marker@2, Replacement} = Entry,
gleam@string:replace(S@1, Marker@2, Replacement)
end
).
-file("src/sqlode/runtime.gleam", 72).
?DOC(
" Prepare a raw query for execution by encoding parameters and expanding\n"
" the engine-agnostic placeholder markers that the generator emits. The\n"
" target placeholder dialect is read from `query.placeholder_style`, so\n"
" callers no longer need to pass a separate style argument.\n"
" Returns the final SQL string and the flattened parameter values, ready\n"
" to be passed to a database driver.\n"
).
-spec prepare(raw_query(HCG), HCG) -> {binary(), list(value())}.
prepare(Query, Params) ->
Values = (erlang:element(7, Query))(Params),
Slices = (erlang:element(8, Query))(Params),
Sql = expand_slice_placeholders(
erlang:element(3, Query),
Slices,
erlang:element(5, Query),
erlang:element(6, Query)
),
{Sql, Values}.