diff --git a/lib/feeb/db.ex b/lib/feeb/db.ex index 2c26bab..cb09b80 100644 --- a/lib/feeb/db.ex +++ b/lib/feeb/db.ex @@ -96,17 +96,20 @@ defmodule Feeb.DB do def one(partial_or_full_query_id, bindings \\ [], opts \\ []) - # TODO: See Feeb.DB, I also support `custom_fields` def one({domain, :fetch}, bindings, opts) when is_list(bindings) do + target_fields = opts[:select] || [:*] + {get_context!(), domain, :__fetch} - |> Query.get_templated_query_id([], %{}) + |> Query.get_templated_query_id(target_fields, %{}) |> one(bindings, opts) end def one({domain, :fetch}, value, opts), do: one({domain, :fetch}, [value], opts) def one({domain, query_name}, bindings, opts) when is_list(bindings) do - one({get_context!(), domain, query_name}, bindings, opts) + {get_context!(), domain, query_name} + |> get_query_id_for_select_query(opts) + |> one(bindings, opts) end def one({domain, query_name}, value, opts), do: one({domain, query_name}, [value], opts) @@ -127,18 +130,22 @@ defmodule Feeb.DB do def all(partial_or_full_query_id, bindings \\ [], opts \\ []) def all(schema, _bindings, opts) when is_atom(schema) do + target_fields = opts[:select] || [:*] + {get_context!(), schema.__table__(), :__all} - |> Query.get_templated_query_id(:all, %{}) + |> Query.get_templated_query_id(target_fields, %{}) |> all([], opts) end - def all({domain, query_name}, bindings, opts) do - all({get_context!(), domain, query_name}, bindings, opts) + def all({domain, query_name}, bindings, opts) when is_list(bindings) do + {get_context!(), domain, query_name} + |> get_query_id_for_select_query(opts) + |> all(bindings, opts) end - def all({_, domain, query_name}, bindings, opts) do - bindings = if is_list(bindings), do: bindings, else: [bindings] + def all({domain, query_name}, value, opts), do: all({domain, query_name}, [value], opts) + def all({_, domain, query_name}, bindings, opts) do case GenServer.call(get_pid!(), {:query, :all, {domain, query_name}, bindings, opts}) do {:ok, rows} -> rows {:error, reason} -> raise reason @@ -147,7 +154,7 @@ defmodule Feeb.DB do def insert(%schema{} = struct, opts \\ []) do {get_context!(), schema.__table__(), :__insert} - |> Query.get_templated_query_id(:all, %{schema: schema}) + |> Query.get_templated_query_id([:*], %{schema: schema}) |> insert_sql(struct, opts) end @@ -288,6 +295,18 @@ defmodule Feeb.DB do LocalState.get_current_context!().context end + defp get_query_id_for_select_query(original_query_id, []), do: original_query_id + + defp get_query_id_for_select_query(original_query_id, opts) do + target_fields = opts[:select] || [:*] + + if target_fields == [:*] do + original_query_id + else + Query.compile_adhoc_query(original_query_id, target_fields) + end + end + defp get_bindings(query_id, struct) do {_, {_, params_bindings}, _} = Query.fetch!(query_id) diff --git a/lib/feeb/db/query.ex b/lib/feeb/db/query.ex index 7aaa1a5..e5aab91 100644 --- a/lib/feeb/db/query.ex +++ b/lib/feeb/db/query.ex @@ -33,24 +33,32 @@ defmodule Feeb.DB.Query do end @doc """ - Compiling an adhoc query is useful when you want to select custom fields - off of a "select *" query. It's like a subset of the original query + Compiling an adhoc query is useful when the user wants to select custom fields off of a `SELECT *` + query. It's essentially a subset of the original query, with specific fields being selected. """ @spec compile_adhoc_query(term, term) :: no_return - def compile_adhoc_query({context, domain, query_name} = query_id, custom_fields) do - raise "Deprecated; consider implementing this feature as part of `get_templated_query_id/3`" - query_name = :"#{query_name}$#{Enum.join(custom_fields, "$")}" - adhoc_query_id = {context, domain, query_name} + def compile_adhoc_query({context, domain, query_name} = original_query_id, target_fields) do + model = Schema.get_model_from_query_id(original_query_id) + valid_fields = model.__cols__() + sorted_target_fields = Enum.sort(target_fields) - {sql, {fields_bindings, params_bindings}, qt} = fetch!(query_id) + adhoc_query_name = :"#{query_name}$#{get_query_name_suffix(sorted_target_fields)}" + adhoc_query_id = {context, domain, adhoc_query_name} + + {sql, {fields_bindings, params_bindings}, qt} = fetch!(original_query_id) if fields_bindings != [:*] do - raise "#{inspect(query_id)}: Custom fields can only be used on 'SELECT *' queries" + raise "#{inspect(original_query_id)}: Custom selection can only be used on 'SELECT *' queries" end - # "Compile" new query - new_sql = String.replace(sql, "*", Enum.join(custom_fields, ", ")) - adhoc_q = {new_sql, {custom_fields, params_bindings}, qt} + Enum.each(target_fields, fn field -> + if field not in valid_fields, + do: raise("Can't select #{inspect(field)}; not a valid field for #{model}") + end) + + # "Compile" new query (replaces `SELECT *` with `SELECT `) + new_sql = String.replace(sql, "*", Enum.join(sorted_target_fields, ", "), global: false) + adhoc_q = {new_sql, {sorted_target_fields, params_bindings}, qt} append_runtime_query(adhoc_query_id, adhoc_q) adhoc_query_id @@ -58,11 +66,26 @@ defmodule Feeb.DB.Query do def get_templated_query_id(query_id, target_fields, meta \\ %{}) + def get_templated_query_id({context, domain, query_name} = query_id, target_fields, _meta) + when query_name in [:__all, :__fetch] do + model = Schema.get_model_from_query_id(query_id) + + real_query_id = {context, domain, :"#{query_name}$#{get_query_name_suffix(target_fields)}"} + + case get(real_query_id) do + {_, _, _} = _compiled_query -> + real_query_id + + nil -> + compile_templated_query(query_name, query_id, target_fields, model) + end + end + def get_templated_query_id({context, domain, :__insert} = query_id, target_fields, _meta) do model = Schema.get_model_from_query_id(query_id) target_fields = - if target_fields == :all do + if target_fields == [:*] do model.__cols__() else raise "Not supported for now, add & test it once needed" @@ -106,8 +129,7 @@ defmodule Feeb.DB.Query do end end - def get_templated_query_id({_context, _domain, query_name} = query_id, target_fields, _meta) - when query_name in [:__all, :__fetch, :__delete] do + def get_templated_query_id({_context, _domain, :__delete} = query_id, target_fields, _meta) do model = Schema.get_model_from_query_id(query_id) case get(query_id) do @@ -115,7 +137,7 @@ defmodule Feeb.DB.Query do query_id nil -> - compile_templated_query(query_name, query_id, target_fields, model) + compile_templated_query(:__delete, query_id, target_fields, model) end end @@ -139,34 +161,32 @@ defmodule Feeb.DB.Query do primary_keys = model.__primary_keys__() assert_adhoc_query!(primary_keys, query_id, model) - set_conditions = - target_fields - |> Enum.reduce([], fn field, acc -> - ["#{field} = ?" | acc] - end) - |> Enum.reverse() - |> Enum.join(", ") + set_clause = generate_update_set_clause(target_fields) + where_clause = generate_where_clause(primary_keys) + sql = "UPDATE #{domain} #{set_clause} #{where_clause};" - sql = "UPDATE #{domain} SET #{set_conditions} #{generate_where_clause(primary_keys)};" adhoc_query = {sql, {[], target_fields ++ primary_keys}, :update} append_runtime_query(query_id, adhoc_query) query_id end - defp compile_templated_query(:__all, {_, domain, _} = query_id, _target_fields, _model) do - sql = "SELECT * FROM #{domain};" - adhoc_query = {sql, {[:*], []}, :select} + defp compile_templated_query(:__all, {_, domain, _} = query_id, target_fields, model) do + sql = "#{generate_select_clause(target_fields, model)} FROM #{domain};" + adhoc_query = {sql, {target_fields, []}, :select} append_runtime_query(query_id, adhoc_query) query_id end - defp compile_templated_query(:__fetch, {_, domain, _} = query_id, _target_fields, model) do + defp compile_templated_query(:__fetch, {_, domain, _} = query_id, target_fields, model) do primary_keys = model.__primary_keys__() assert_adhoc_query!(primary_keys, query_id, model) - sql = "SELECT * FROM #{domain} #{generate_where_clause(primary_keys)};" - adhoc_query = {sql, {[:*], primary_keys}, :select} + select_clause = generate_select_clause(target_fields, model) + where_clause = generate_where_clause(primary_keys) + sql = "#{select_clause} FROM #{domain} #{where_clause};" + + adhoc_query = {sql, {target_fields, primary_keys}, :select} append_runtime_query(query_id, adhoc_query) query_id end @@ -232,6 +252,16 @@ defmodule Feeb.DB.Query do end end + defp get_query_name_suffix(target_fields) when is_list(target_fields) do + target_fields + |> Enum.sort() + |> Enum.reduce([], fn field, acc -> + ["#{field}" | acc] + end) + |> Enum.reverse() + |> Enum.join("$") + end + defp maybe_inject_returning_clause(nil, _), do: nil defp maybe_inject_returning_clause(query, []), do: query @@ -253,6 +283,37 @@ defmodule Feeb.DB.Query do defp get_returning_fields({_, _, operation}) when operation in [:update, :delete], do: "*" + defp generate_select_clause([:*], _), do: "SELECT *" + + defp generate_select_clause(fields, model) when is_list(fields) do + valid_fields = model.__cols__() + + select_conditions = + fields + |> Enum.reduce([], fn field, acc -> + if field not in valid_fields, + do: raise("Can't select #{inspect(field)}; not a valid field for #{model}") + + ["#{field}" | acc] + end) + |> Enum.reverse() + |> Enum.join(", ") + + "SELECT #{select_conditions}" + end + + defp generate_update_set_clause(fields) when is_list(fields) do + set_conditions = + fields + |> Enum.reduce([], fn field, acc -> + ["#{field} = ?" | acc] + end) + |> Enum.reverse() + |> Enum.join(", ") + + "SET #{set_conditions}" + end + defp generate_where_clause(primary_keys) when is_list(primary_keys) do where_conditions = primary_keys diff --git a/lib/feeb/db/repo.ex b/lib/feeb/db/repo.ex index fbf8de5..8046818 100644 --- a/lib/feeb/db/repo.ex +++ b/lib/feeb/db/repo.ex @@ -283,8 +283,8 @@ defmodule Feeb.DB.Repo do defp format_result(:one, _, _, [row], _, %{format: :raw}), do: {:ok, row} - defp format_result(:one, query_id, query, [row], _, %{format: :type}), - do: {:ok, create_types_from_rows(query_id, query, [row]) |> List.first()} + defp format_result(:one, query_id, query, [row], _, %{format: :map}), + do: {:ok, create_maps_from_rows(query_id, query, [row]) |> List.first()} defp format_result(:one, query_id, query, [row], _, _), do: {:ok, create_schema_from_rows(query_id, query, [row]) |> List.first()} @@ -295,13 +295,13 @@ defmodule Feeb.DB.Repo do defp format_result(:all, _, _, [], _, _), do: {:ok, []} defp format_result(:all, _, _, rows, _, %{format: :raw}), do: {:ok, rows} - defp format_result(:all, query_id, query, rows, _, %{format: :type}), - do: {:ok, create_types_from_rows(query_id, query, rows)} + defp format_result(:all, query_id, query, rows, _, %{format: :map}), + do: {:ok, create_maps_from_rows(query_id, query, rows)} defp format_result(:all, query_id, query, rows, _, %{format: :schema}), do: {:ok, create_schema_from_rows(query_id, query, rows)} - defp format_result(:insert, _, _, [], _, %{format: format}) when format in [:raw, :type], + defp format_result(:insert, _, _, [], _, %{format: format}) when format in [:raw, :map], do: {:ok, nil} defp format_result(:insert, query_id, query, [], bindings, %{format: :schema}) do @@ -364,7 +364,7 @@ defmodule Feeb.DB.Repo do Enum.map(rows, fn row -> Schema.from_row(model, model.__cols__(), row) end) end - defp create_types_from_rows(query_id, {_, {fields_bindings, _}, :select} = query, rows) do + defp create_maps_from_rows(query_id, {_, {fields_bindings, _}, :select} = query, rows) do # Performance-wise, not the best solution, but I'd rather keep the code readable for a bit # longer. Simply create the full schema and use only the fields the user selected create_schema_from_rows(query_id, query, rows) diff --git a/lib/feeb/db/schema.ex b/lib/feeb/db/schema.ex index 98d88f1..fedd12c 100644 --- a/lib/feeb/db/schema.ex +++ b/lib/feeb/db/schema.ex @@ -126,7 +126,8 @@ defmodule Feeb.DB.Schema do assert_env.(:schema) end - defmacro cast(args, target_fields \\ unquote(:all)) do + # TODO: Why is this a macro? + defmacro cast(args, target_fields \\ unquote([:*])) do quote do meta = %{ valid?: true, diff --git a/priv/test/queries/test/all_types.sql b/priv/test/queries/test/all_types.sql index c30f2d6..cfde0ab 100644 --- a/priv/test/queries/test/all_types.sql +++ b/priv/test/queries/test/all_types.sql @@ -7,6 +7,9 @@ select map_keys_atom from all_types; -- :get_map select map from all_types; +-- :get_by_integer +select * from all_types where integer = ?; + -- :get_max_integer select max(integer) from all_types; diff --git a/test/db/db_test.exs b/test/db/db_test.exs index 6f8e539..47f8bab 100644 --- a/test/db/db_test.exs +++ b/test/db/db_test.exs @@ -2,6 +2,7 @@ defmodule Feeb.DBTest do use Test.Feeb.DBCase, async: true alias Feeb.DB, as: DB alias Feeb.DB.LocalState + alias Feeb.DB.Value.NotLoaded alias Sample.{AllTypes, CustomTypes, Friend, OrderItems, Post} alias Sample.Types.TypedID @@ -398,7 +399,7 @@ defmodule Feeb.DBTest do end end - describe "one/1" do + describe "one/3" do test "returns the expected result", %{shard_id: shard_id} do DB.begin(@context, shard_id, :read) assert %{id: 1, name: "Phoebe"} = DB.one({:friends, :get_by_id}, [1]) @@ -406,14 +407,21 @@ defmodule Feeb.DBTest do assert nil == DB.one({:friends, :get_by_id}, [0]) end - test ":fetch templated query works", %{shard_id: shard_id} do + test ":__fetch templated query works", %{shard_id: shard_id} do DB.begin(@context, shard_id, :write) assert %{id: 1, name: "Phoebe"} = DB.one({:friends, :fetch}, [1]) assert nil == DB.one({:friends, :fetch}, [500]) end - test ":fetch templated query works on schema with composite PKs", %{shard_id: shard_id} do + test ":__fetch templated query with custom selection", %{shard_id: shard_id} do + DB.begin(@context, shard_id, :write) + + assert %{id: %NotLoaded{}, name: "Phoebe"} = DB.one({:friends, :fetch}, [1], select: [:name]) + assert nil == DB.one({:friends, :fetch}, [500], select: [:name]) + end + + test ":__fetch templated query works on schema with composite PKs", %{shard_id: shard_id} do DB.begin(@context, shard_id, :write) %{order_id: 1, product_id: 2, quantity: 10, price: 50} @@ -429,6 +437,13 @@ defmodule Feeb.DBTest do assert nil == DB.one({:order_items, :fetch}, [2, 1]) end + test "supports custom selection on user-defined queries", %{shard_id: shard_id} do + DB.begin(@context, shard_id, :read) + + assert %{id: %NotLoaded{}, name: "Phoebe"} = + DB.one({:friends, :get_by_name}, "Phoebe", select: [:name]) + end + test "supports the :format flag", %{shard_id: shard_id} do DB.begin(@context, shard_id, :write) @@ -447,12 +462,20 @@ defmodule Feeb.DBTest do assert ["i_am_atom", 50] == DB.one({:all_types, :get_atom_and_integer}, [], format: :raw) assert ["{\"foo\":\"bar\"}"] == DB.one({:all_types, :get_map_keys_atom}, [], format: :raw) - # With the :type flag, we return the values formatted by their types _without_ the full schema + # With the :map flag, we return the values formatted by their types _without_ the full schema assert %{map_keys_atom: %{foo: "bar"}} == - DB.one({:all_types, :get_map_keys_atom}, [], format: :type) + DB.one({:all_types, :get_map_keys_atom}, [], format: :map) assert %{atom: :i_am_atom, integer: 50} == - DB.one({:all_types, :get_atom_and_integer}, [], format: :type) + DB.one({:all_types, :get_atom_and_integer}, [], format: :map) + + # Custom selection with different formats + assert ["Phoebe"] = DB.one({:friends, :fetch}, [1], select: [:name], format: :raw) + assert %{name: "Phoebe"} = DB.one({:friends, :fetch}, [1], select: [:name], format: :map) + + # No matching results + assert nil == DB.one({:friends, :fetch}, [500], select: [:name], format: :raw) + assert nil == DB.one({:friends, :fetch}, [500], select: [:name], format: :map) end test "works with window functions when using :raw flag", %{shard_id: shard_id} do @@ -505,6 +528,36 @@ defmodule Feeb.DBTest do end describe "all/3" do + test ":__all templated query works", %{shard_id: shard_id} do + DB.begin(@context, shard_id, :write) + + # There are 6 friends + assert [_, _, _, _, _, _] = DB.all(Friend) + + %{id: 1, title: "My Post", body: "My Body"} + |> Post.new() + |> DB.insert!() + + assert [%{id: 1, title: "My Post", body: "My Body"}] = DB.all(Post) + end + + test ":__all templated query works with custom selection", %{shard_id: shard_id} do + DB.begin(@context, shard_id, :write) + + %{id: 1, title: "Post", body: "My Body"} + |> Post.new() + |> DB.insert!() + + assert [%{id: 1, title: "Post", body: %NotLoaded{}}] = DB.all(Post, [], select: [:id, :title]) + end + + test "supports custom selection on user-defined queries", %{shard_id: shard_id} do + DB.begin(@context, shard_id, :read) + + assert [%{id: %NotLoaded{}, name: "Phoebe"}] = + DB.all({:friends, :get_by_name}, "Phoebe", select: [:name]) + end + test "supports the :format flag", %{shard_id: shard_id} do DB.begin(@context, shard_id, :write) @@ -530,12 +583,21 @@ defmodule Feeb.DBTest do assert [["{\"foo\":\"bar\"}"], ["{\"girl\":[\"so\",\"confusing\"]}"]] |> Enum.sort() == DB.all({:all_types, :get_map}, [], format: :raw) |> Enum.sort() - # With the :type flag, we return the values formatted by their types _without_ the full schema + # With the :map flag, we return the values formatted by their types _without_ the full schema assert [%{map: %{girl: ["so", "confusing"]}}, %{map: %{foo: "bar"}}] |> Enum.sort() == - DB.all({:all_types, :get_map}, [], format: :type) |> Enum.sort() + DB.all({:all_types, :get_map}, [], format: :map) |> Enum.sort() assert [%{atom: :i_am_atom, integer: 50}, %{atom: :other_atom, integer: -2}] |> Enum.sort() == - DB.all({:all_types, :get_atom_and_integer}, [], format: :type) |> Enum.sort() + DB.all({:all_types, :get_atom_and_integer}, [], format: :map) |> Enum.sort() + + # Custom selection with different formats + assert [%{name: "Phoebe"}] = + DB.all({:friends, :get_by_name}, "Phoebe", select: [:name], format: :map) + + assert [["Joey"]] = DB.all({:friends, :get_by_name}, "Joey", select: [:name], format: :raw) + + # No matching results + assert [] == DB.all({:friends, :get_by_name}, "Michael Scott", format: :map) end end diff --git a/test/db/query_test.exs b/test/db/query_test.exs index 5a3b333..364b089 100644 --- a/test/db/query_test.exs +++ b/test/db/query_test.exs @@ -1,7 +1,7 @@ defmodule Feeb.DB.QueryTest do # Reason for `async: false`: this test suite interacts directly with the compiled queries cache. # While it's technically possible to adapt the cache to be per-test, I don't think it's worth the - # added complexity. As such, I'd rather have this test suite run separately from the rest. + # added complexity. As such, I'd rather have this test suite running separately from the rest. use ExUnit.Case, async: false import ExUnit.CaptureLog @@ -48,13 +48,55 @@ defmodule Feeb.DB.QueryTest do end end + describe "compile_adhoc_query/2" do + test "generates a subset of the original query" do + Query.compile(@all_types_path, {:test, :all_types}) + + original_query_id = {:test, :all_types, :get_by_integer} + query_id = Query.compile_adhoc_query(original_query_id, [:string, :atom, :uuid]) + + assert {sql, {target_fields, bindings}, query_type} = Query.fetch!(query_id, []) + assert query_type == :select + assert target_fields == [:atom, :string, :uuid] + assert bindings == [:integer] + assert sql == "select atom, string, uuid from all_types where integer = ?;" + end + + test "raises on custom selection of a non-wildcard select query" do + Query.compile(@all_types_path, {:test, :all_types}) + + # This query is: `SELECT atom, integer FROM all_types;` + original_query_id = {:test, :all_types, :get_atom_and_integer} + + %{message: error} = + assert_raise RuntimeError, fn -> + Query.compile_adhoc_query(original_query_id, [:string, :atom, :uuid]) + end + + assert error =~ "Custom selection can only be used on 'SELECT *' queries" + end + + test "raises when trying to select a field that does not exist in the schema" do + Query.compile(@all_types_path, {:test, :all_types}) + + original_query_id = {:test, :all_types, :get_by_integer} + + %{message: error} = + assert_raise RuntimeError, fn -> + Query.compile_adhoc_query(original_query_id, [:string, :i_dont_exist, :atom]) + end + + assert error =~ "Can't select :i_dont_exist; not a valid field for Elixir.Sample.AllTypes" + end + end + describe "get_templated_query_id/3" do test ":__all" do Query.compile(@friends_path, {:test, :friends}) # Ensures the :__fetch is compiled (due to it being an "ad-hoc" query) query_id = {:test, :friends, :__all} - Query.get_templated_query_id(query_id, []) + Query.get_templated_query_id(query_id, [:*]) assert {sql, {target_fields, bindings}, query_type} = Query.fetch!(query_id) assert query_type == :select @@ -63,11 +105,23 @@ defmodule Feeb.DB.QueryTest do assert sql == "SELECT * FROM friends;" end + test ":__all - with custom target fields" do + Query.compile(@friends_path, {:test, :friends}) + + query_id = Query.get_templated_query_id({:test, :friends, :__all}, [:name]) + + assert {sql, {target_fields, bindings}, query_type} = Query.fetch!(query_id) + assert query_type == :select + assert target_fields == [:name] + assert bindings == [] + assert sql == "SELECT name FROM friends;" + end + test ":__fetch" do Query.compile(@friends_path, {:test, :friends}) query_id = {:test, :friends, :__fetch} - Query.get_templated_query_id(query_id, []) + Query.get_templated_query_id(query_id, [:*]) assert {sql, {target_fields, bindings}, query_type} = Query.fetch!(query_id) assert query_type == :select @@ -80,7 +134,7 @@ defmodule Feeb.DB.QueryTest do Query.compile(@order_items_path, {:test, :order_items}) query_id = {:test, :order_items, :__fetch} - Query.get_templated_query_id(query_id, []) + Query.get_templated_query_id(query_id, [:*]) assert {sql, {target_fields, bindings}, query_type} = Query.fetch!(query_id) assert query_type == :select @@ -89,6 +143,29 @@ defmodule Feeb.DB.QueryTest do assert sql == "SELECT * FROM order_items WHERE order_id = ? AND product_id = ?;" end + test ":__fetch - with custom target fields" do + Query.compile(@order_items_path, {:test, :order_items}) + + query_id = Query.get_templated_query_id({:test, :order_items, :__fetch}, [:quantity, :price]) + + assert {sql, {target_fields, bindings}, query_type} = Query.fetch!(query_id) + assert sql == "SELECT quantity, price FROM order_items WHERE order_id = ? AND product_id = ?;" + assert target_fields == [:quantity, :price] + assert bindings == [:order_id, :product_id] + assert query_type == :select + end + + test ":__fetch - raises when invalid fields are selected" do + Query.compile(@order_items_path, {:test, :order_items}) + + %{message: error} = + assert_raise RuntimeError, fn -> + Query.get_templated_query_id({:test, :order_items, :__fetch}, [:i_dont_exist]) + end + + assert error =~ "Can't select :i_dont_exist; not a valid field for Elixir.Sample.OrderItems" + end + test ":__fetch - raises when schema has no PK" do Query.compile(@all_types_path, {:test, :all_types}) @@ -96,7 +173,7 @@ defmodule Feeb.DB.QueryTest do %{message: error} = assert_raise RuntimeError, fn -> - Query.get_templated_query_id(query_id, []) + Query.get_templated_query_id(query_id, [:*]) end assert error =~ "Can't generate adhoc query" @@ -108,7 +185,7 @@ defmodule Feeb.DB.QueryTest do Query.compile(@friends_path, {:test, :friends}) query_id = {:test, :friends, :__insert} - Query.get_templated_query_id(query_id, :all) + Query.get_templated_query_id(query_id, [:*]) assert {sql, {target_fields, bindings}, query_type} = Query.fetch!(query_id) assert query_type == :insert