-module(kura_ets_dialect). -moduledoc """ "Dialect" for the ETS backend. There is no SQL to emit: each callback wraps the portable `#kura_query{}` AST (or the operation data) into an opaque `{kura_ets, Plan}` term that `kura_ets_driver` hands to `kura_ets_query` for interpretation against ETS tables. The parameter list is always `[]`: values stay embedded in the plan. This keeps the plan a pure function of the query AST, so kura's query cache (`kura_query_compiler:to_sql_cached/2`, keyed on the hashed AST) remains correct. One observable difference vs SQL dialects: kura's query telemetry and legacy logging stringify the compiled statement with `iolist_to_binary/1`, which does not accept a plan tuple; those events are skipped for this backend (kura wraps emission in a catch). """. -behaviour(kura_dialect). -include_lib("kura/include/kura.hrl"). -export([ to_sql/1, to_sql_from/2, insert/3, insert/4, update/4, delete/2, update_all/2, delete_all/1, insert_all/3, insert_all/4 ]). -type plan() :: {select, #kura_query{}} | {insert, atom() | module(), map(), map()} | {update, atom() | module(), map(), [{atom(), term()}]} | {delete, atom() | module(), [{atom(), term()}]} | {update_all, #kura_query{}, map()} | {delete_all, #kura_query{}} | {insert_all, atom() | module(), [map()], map()}. -export_type([plan/0]). %%---------------------------------------------------------------------- %% kura_dialect callbacks %%---------------------------------------------------------------------- -spec to_sql(#kura_query{}) -> {dynamic(), [term()]}. to_sql(Query) -> {{kura_ets, {select, Query}}, []}. -spec to_sql_from(#kura_query{}, pos_integer()) -> {dynamic(), [term()], pos_integer()}. to_sql_from(Query, StartCounter) -> %% Sub-query composition (CTEs, IN (subquery)) is rejected at %% execution time; the callback exists to satisfy the behaviour. {{kura_ets, {select, Query}}, [], StartCounter}. -spec insert(atom() | module(), [atom()], map()) -> {dynamic(), [term()]}. insert(SchemaOrTable, Fields, Data) -> insert(SchemaOrTable, Fields, Data, #{}). -spec insert(atom() | module(), [atom()], map(), map()) -> {dynamic(), [term()]}. insert(SchemaOrTable, _Fields, Data, Opts) -> {{kura_ets, {insert, SchemaOrTable, Data, Opts}}, []}. -spec update(atom() | module(), [atom()], map(), [{atom(), term()}]) -> {dynamic(), [term()]}. update(SchemaOrTable, _Fields, Changes, KeyClauses) -> {{kura_ets, {update, SchemaOrTable, Changes, KeyClauses}}, []}. -spec delete(atom() | module(), [{atom(), term()}]) -> {dynamic(), [term()]}. delete(SchemaOrTable, KeyClauses) -> {{kura_ets, {delete, SchemaOrTable, KeyClauses}}, []}. -spec update_all(#kura_query{}, map()) -> {dynamic(), [term()]}. update_all(Query, SetMap) -> {{kura_ets, {update_all, Query, SetMap}}, []}. -spec delete_all(#kura_query{}) -> {dynamic(), [term()]}. delete_all(Query) -> {{kura_ets, {delete_all, Query}}, []}. -spec insert_all(atom() | module(), [atom()], [map()]) -> {dynamic(), [term()]}. insert_all(SchemaOrTable, Fields, Rows) -> insert_all(SchemaOrTable, Fields, Rows, #{}). -spec insert_all(atom() | module(), [atom()], [map()], map()) -> {dynamic(), [term()]}. insert_all(SchemaOrTable, _Fields, Rows, Opts) -> {{kura_ets, {insert_all, SchemaOrTable, Rows, Opts}}, []}.