The bug
Writing an {:array, :map} attribute through an update action on AshSqlite.DataLayer fails
with a SQL syntax error:
** (Exqlite.Error) near "[?,?]": syntax error
UPDATE "visual_machines" AS v0 SET ..., "connections" = ARRAY[?], "nodes" = ARRAY[?,?]
AshSql.Expr.encode_list/6 renders every list literal as ARRAY[...] (or
array_to_json(ARRAY[...])), and default_dynamic_expr/6 routes a bare list to list_expr/6
without consulting bindings.sql_behaviour.expr/6 first. So unlike map comparisons, which an
adapter can intercept with its own expr/6 clause (that is how ash_sqlite JSON-encodes them,
and how ash-project/ash_sqlite#220 extends that to is_distinct_from), the adapter never sees
a list literal at all. There is no seam to implement.
This is independent of atomicity and of #220: the rendering applies to the plain
SET col = ... clause as well.
Minimal repro
defmodule Repro.Thing do
use Ash.Resource, domain: Repro.Domain, data_layer: AshSqlite.DataLayer
sqlite do
table "things"
repo Repro.Repo
end
attributes do
uuid_primary_key :id
attribute :items, {:array, :map}, public?: true
end
actions do
defaults [:read, :create]
update :update, primary?: true, accept: [:items]
end
end
{:ok, thing} = Ash.create(Repro.Thing, %{})
# raises: ** (Exqlite.Error) near "[?]": syntax error
{:ok, _} = Ash.update(thing, %{items: [%{"a" => 1}]})
Suggested shape
A callback on AshSql.Implementation for rendering list literals (defaulting to the current
ARRAY[...] behaviour so Postgres is untouched), consulted by encode_list/6 / list_expr/6,
so ash_sqlite can emit json_array(...) or a JSON-encoded parameter instead. Happy to open a PR
along those lines if that shape is acceptable, or to adapt to whatever seam you'd prefer.
Verified against ash 3.31.3 / ash_sql 0.6.8 / ash_sqlite 0.2.17 / exqlite 0.36.0
(SQLite 3.51.3), where 18 (resource, update action, {:array, :map} attribute) triples across
8 resources fail this way; each is exercised against a real database rather than verified by
reading the code.
The bug
Writing an
{:array, :map}attribute through an update action onAshSqlite.DataLayerfailswith a SQL syntax error:
AshSql.Expr.encode_list/6renders every list literal asARRAY[...](orarray_to_json(ARRAY[...])), anddefault_dynamic_expr/6routes a bare list tolist_expr/6without consulting
bindings.sql_behaviour.expr/6first. So unlike map comparisons, which anadapter can intercept with its own
expr/6clause (that is how ash_sqlite JSON-encodes them,and how ash-project/ash_sqlite#220 extends that to
is_distinct_from), the adapter never seesa list literal at all. There is no seam to implement.
This is independent of atomicity and of #220: the rendering applies to the plain
SET col = ...clause as well.Minimal repro
Suggested shape
A callback on
AshSql.Implementationfor rendering list literals (defaulting to the currentARRAY[...]behaviour so Postgres is untouched), consulted byencode_list/6/list_expr/6,so ash_sqlite can emit
json_array(...)or a JSON-encoded parameter instead. Happy to open a PRalong those lines if that shape is acceptable, or to adapt to whatever seam you'd prefer.
Verified against ash 3.31.3 / ash_sql 0.6.8 / ash_sqlite 0.2.17 / exqlite 0.36.0
(SQLite 3.51.3), where 18
(resource, update action, {:array, :map} attribute)triples across8 resources fail this way; each is exercised against a real database rather than verified by
reading the code.