diff --git a/lib/ecto/adapters/clickhouse/api.ex b/lib/ecto/adapters/clickhouse/api.ex index 7886b0d..851013d 100644 --- a/lib/ecto/adapters/clickhouse/api.ex +++ b/lib/ecto/adapters/clickhouse/api.ex @@ -48,4 +48,35 @@ defmodule Ecto.Adapters.ClickHouse.API do # %{query | sources: {nil, schema}} query end + + @doc """ + Builds a common table expression with an (constant) expression instead of a subquery. + + `with_query` must be a fragment to be evaluated as an expression. + + ## Options + + - as: must be a compile-time literal string that is used in the main query to select + the static value. + + https://clickhouse.com/docs/en/sql-reference/statements/select/with#syntax + """ + defmacro with_cte_expression(query, with_query, opts) do + name = opts[:as] + + if !name do + Ecto.Query.Builder.error!("`as` option must be specified") + end + + # :HACK: We override the operation to :update_all to pass context to the connection. + # :update_all is not used within ClickHouse adapter otherwise. + Ecto.Query.Builder.CTE.build( + query, + name, + with_query, + opts[:materialized], + :update_all, + __CALLER__ + ) + end end diff --git a/lib/ecto/adapters/clickhouse/connection.ex b/lib/ecto/adapters/clickhouse/connection.ex index b513c81..45bce10 100644 --- a/lib/ecto/adapters/clickhouse/connection.ex +++ b/lib/ecto/adapters/clickhouse/connection.ex @@ -369,8 +369,12 @@ defmodule Ecto.Adapters.ClickHouse.Connection do recursive_opt = if recursive, do: "RECURSIVE ", else: "" ctes = - intersperse_map(queries, ?,, fn {name, _opts, cte} -> - [quote_name(name), " AS ", cte_query(cte, sources, params, query)] + intersperse_map(queries, ?,, fn {name, opts, cte} -> + if opts[:operation] == :update_all do + [cte_query(cte, sources, params, query), " AS ", quote_name(name)] + else + [quote_name(name), " AS ", cte_query(cte, sources, params, query)] + end end) ["WITH ", recursive_opt, ctes, " "] diff --git a/test/ecto/adapters/clickhouse/connection_test.exs b/test/ecto/adapters/clickhouse/connection_test.exs index 2efa567..3880630 100644 --- a/test/ecto/adapters/clickhouse/connection_test.exs +++ b/test/ecto/adapters/clickhouse/connection_test.exs @@ -7,6 +7,8 @@ defmodule Ecto.Adapters.ClickHouse.ConnectionTest do import Ecto.Query import Ecto.Migration, only: [table: 1, table: 2, index: 3, constraint: 3] + require Ecto.Adapters.ClickHouse.API + import Ecto.Adapters.ClickHouse.API, only: [with_cte_expression: 3] defmodule Comment do use Ecto.Schema @@ -233,6 +235,15 @@ defmodule Ecto.Adapters.ClickHouse.ConnectionTest do """ end + test "common table expression with expression instead of subquery" do + query = + Schema + |> with_cte_expression(fragment("123"), as: "value") + |> select([], fragment("value")) + + assert all(query) == "WITH 123 AS \"value\" SELECT value FROM \"schema\" AS s0" + end + test "reference common table in union" do comments_scope_query = "comments"