Skip to content

No adapter seam for list literals: encode_list/6 always renders Postgres ARRAY[...], which is a syntax error on SQLite #246

Description

@AlanMcCann

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions