zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

connection.ex (54133B)


      1 if Code.ensure_loaded?(Tds) do
      2   defmodule Ecto.Adapters.Tds.Connection do
      3     @moduledoc false
      4     require Logger
      5     alias Tds.Query
      6     alias Ecto.Query.Tagged
      7     alias Ecto.Adapters.SQL
      8     require Ecto.Schema
      9 
     10     @behaviour Ecto.Adapters.SQL.Connection
     11 
     12     @impl true
     13     def child_spec(opts) do
     14       opts
     15       |> Keyword.put_new(:use_elixir_calendar_types, true)
     16       |> Tds.child_spec()
     17     end
     18 
     19     @impl true
     20     def prepare_execute(pid, _name, statement, params, opts \\ []) do
     21       query = %Query{statement: statement}
     22       params = prepare_params(params)
     23 
     24       opts = Keyword.put(opts, :parameters, params)
     25       DBConnection.prepare_execute(pid, query, params, opts)
     26     end
     27 
     28     @impl true
     29     def execute(pid, statement, params, opts) when is_binary(statement) or is_list(statement) do
     30       query = %Query{statement: statement}
     31       params = prepare_params(params)
     32       opts = Keyword.put(opts, :parameters, params)
     33 
     34       case DBConnection.prepare_execute(pid, query, params, opts) do
     35         {:ok, _, %Tds.Result{columns: nil, num_rows: num_rows, rows: []}}
     36         when num_rows >= 0 ->
     37           {:ok, %Tds.Result{columns: nil, num_rows: num_rows, rows: nil}}
     38 
     39         {:ok, _, query} ->
     40           {:ok, query}
     41 
     42         {:error, _} = err ->
     43           err
     44       end
     45     end
     46 
     47     def execute(pid, %{} = query, params, opts) do
     48       opts = Keyword.put_new(opts, :parameters, params)
     49       params = prepare_params(params)
     50       opts = Keyword.put(opts, :parameters, params)
     51 
     52       case DBConnection.prepare_execute(pid, query, params, opts) do
     53         {:ok, _, query} -> {:ok, query}
     54         {:error, _} = err -> err
     55       end
     56     end
     57 
     58     @impl true
     59     def stream(_conn, _sql, _params, _opts) do
     60       error!(nil, "Repo.stream is not supported in the Tds adapter")
     61     end
     62 
     63     @impl true
     64     def query(conn, sql, params, opts) do
     65       params = prepare_params(params)
     66       Tds.query(conn, sql, params, opts)
     67     end
     68 
     69     @impl true
     70     def query_many(_conn, _sql, _params, _opts) do
     71       error!(nil, "query_many is not supported in the Tds adapter")
     72     end
     73 
     74     @impl true
     75     def to_constraints(%Tds.Error{mssql: %{number: code, msg_text: message}}, _opts) do
     76       Tds.Error.get_constraint_violations(code, message)
     77     end
     78 
     79     def to_constraints(_, _opts), do: []
     80 
     81     defp prepare_params(params) do
     82       {params, _} =
     83         Enum.map_reduce(params, 1, fn param, acc ->
     84           {value, type} = prepare_param(param)
     85           {%Tds.Parameter{name: "@#{acc}", value: value, type: type}, acc + 1}
     86         end)
     87 
     88       params
     89     end
     90 
     91     # Decimal
     92     defp prepare_param(%Decimal{} = value) do
     93       {value, :decimal}
     94     end
     95 
     96     defp prepare_param(%NaiveDateTime{} = value) do
     97       {value, :datetime2}
     98     end
     99 
    100     defp prepare_param(%DateTime{} = value) do
    101       {value, :datetimeoffset}
    102     end
    103 
    104     defp prepare_param(%Date{} = value) do
    105       {value, :date}
    106     end
    107 
    108     defp prepare_param(%Time{} = value) do
    109       {value, :time}
    110     end
    111 
    112     defp prepare_param(%{__struct__: module} = _value) do
    113       # just in case dumpers/loaders are not defined for the this struct
    114       error!(
    115         nil,
    116         "Tds adapter is unable to convert struct `#{inspect(module)}` into supported MSSQL types"
    117       )
    118     end
    119 
    120     defp prepare_param(%{} = value), do: {json_library().encode!(value), :string}
    121     defp prepare_param(value), do: prepare_raw_param(value)
    122 
    123     defp prepare_raw_param(value) when is_binary(value) do
    124       type = if String.printable?(value), do: :string, else: :binary
    125       {value, type}
    126     end
    127 
    128     defp prepare_raw_param(value) when value == true, do: {1, :boolean}
    129     defp prepare_raw_param(value) when value == false, do: {0, :boolean}
    130     defp prepare_raw_param({_, :varchar} = value), do: value
    131     defp prepare_raw_param(value), do: {value, nil}
    132 
    133     defp json_library(), do: Application.get_env(:tds, :json_library, Jason)
    134 
    135     ## Query
    136 
    137     @parent_as __MODULE__
    138     alias Ecto.Query
    139     alias Ecto.Query.{BooleanExpr, JoinExpr, QueryExpr, WithExpr}
    140 
    141     @impl true
    142     def all(query, as_prefix \\ []) do
    143       sources = create_names(query, as_prefix)
    144 
    145       cte = cte(query, sources)
    146       from = from(query, sources)
    147       select = select(query, sources)
    148       join = join(query, sources)
    149       where = where(query, sources)
    150       group_by = group_by(query, sources)
    151       having = having(query, sources)
    152       _window = window(query, sources)
    153       combinations = combinations(query)
    154       order_by = order_by(query, sources)
    155       # limit = is handled in select (TOP X)
    156       offset = offset(query, sources)
    157       lock = lock(query, sources)
    158 
    159       if query.offset != nil and query.order_bys == [],
    160         do: error!(query, "ORDER BY is mandatory when OFFSET is set")
    161 
    162       [cte, select, from, join, where, group_by, having, combinations, order_by, lock | offset]
    163     end
    164 
    165     @impl true
    166     def update_all(query) do
    167       sources = create_names(query, [])
    168       cte = cte(query, sources)
    169       {table, name, _model} = elem(sources, 0)
    170 
    171       fields = update_fields(query, sources)
    172       from = " FROM #{table} AS #{name}"
    173       join = join(query, sources)
    174       where = where(query, sources)
    175       lock = lock(query, sources)
    176 
    177       [
    178         cte,
    179         "UPDATE ",
    180         name,
    181         " SET ",
    182         fields,
    183         returning(query, 0, "INSERTED"),
    184         from,
    185         join,
    186         where | lock
    187       ]
    188     end
    189 
    190     @impl true
    191     def delete_all(query) do
    192       sources = create_names(query, [])
    193       cte = cte(query, sources)
    194       {table, name, _model} = elem(sources, 0)
    195 
    196       delete = "DELETE #{name}"
    197       from = " FROM #{table} AS #{name}"
    198       join = join(query, sources)
    199       where = where(query, sources)
    200       lock = lock(query, sources)
    201 
    202       [cte, delete, returning(query, 0, "DELETED"), from, join, where | lock]
    203     end
    204 
    205     @impl true
    206     def insert(prefix, table, header, rows, on_conflict, returning, placeholders) do
    207       counter_offset = length(placeholders) + 1
    208       [] = on_conflict(on_conflict, header)
    209       returning = returning(returning, "INSERTED")
    210 
    211       values =
    212         if header == [] do
    213           [returning, " DEFAULT VALUES"]
    214         else
    215           [
    216             ?\s,
    217             ?(,
    218             quote_names(header),
    219             ?),
    220             returning |
    221             insert_all(rows, counter_offset)
    222           ]
    223         end
    224 
    225       ["INSERT INTO ", quote_table(prefix, table), values]
    226     end
    227 
    228     defp on_conflict({:raise, _, []}, _header) do
    229       []
    230     end
    231 
    232     defp on_conflict({_, _, _}, _header) do
    233       error!(nil, "Tds adapter supports only on_conflict: :raise")
    234     end
    235 
    236     defp insert_all(%Ecto.Query{} = query, _counter) do
    237       [?\s, all(query)]
    238     end
    239     defp insert_all(rows, counter) do
    240       sql =
    241         intersperse_reduce(rows, ",", counter, fn row, counter ->
    242           {row, counter} = insert_each(row, counter)
    243           {[?(, row, ?)], counter}
    244         end)
    245         |> elem(0)
    246 
    247       [" VALUES " | sql]
    248     end
    249 
    250     defp insert_each(values, counter) do
    251       intersperse_reduce(values, ", ", counter, fn
    252         nil, counter ->
    253           {"DEFAULT", counter}
    254 
    255         {%Query{} = query, params_counter}, counter ->
    256           {[?(, all(query), ?)], counter + params_counter}
    257 
    258         {:placeholder, placeholder_index}, counter ->
    259           {[?@ | placeholder_index], counter}
    260 
    261         _, counter ->
    262           {[?@ | Integer.to_string(counter)], counter + 1}
    263       end)
    264     end
    265 
    266     @impl true
    267     def update(prefix, table, fields, filters, returning) do
    268       {fields, count} =
    269         intersperse_reduce(fields, ", ", 1, fn field, acc ->
    270           {[quote_name(field), " = @", Integer.to_string(acc)], acc + 1}
    271         end)
    272 
    273       {filters, _count} =
    274         intersperse_reduce(filters, " AND ", count, fn
    275           {field, nil}, acc ->
    276             {[quote_name(field), " IS NULL"], acc + 1}
    277 
    278           {field, _value}, acc ->
    279             {[quote_name(field), " = @", Integer.to_string(acc)], acc + 1}
    280 
    281           field, acc ->
    282             {[quote_name(field), " = @", Integer.to_string(acc)], acc + 1}
    283         end)
    284 
    285       [
    286         "UPDATE ",
    287         quote_table(prefix, table),
    288         " SET ",
    289         fields,
    290         returning(returning, "INSERTED"),
    291         " WHERE " | filters
    292       ]
    293     end
    294 
    295     @impl true
    296     def delete(prefix, table, filters, returning) do
    297       {filters, _} =
    298         intersperse_reduce(filters, " AND ", 1, fn
    299           {field, nil}, acc ->
    300             {[quote_name(field), " IS NULL"], acc + 1}
    301 
    302           {field, _value}, acc ->
    303             {[quote_name(field), " = @", Integer.to_string(acc)], acc + 1}
    304 
    305           field, acc ->
    306             {[quote_name(field), " = @", Integer.to_string(acc)], acc + 1}
    307         end)
    308 
    309       [
    310         "DELETE FROM ",
    311         quote_table(prefix, table),
    312         returning(returning, "DELETED"),
    313         " WHERE " | filters
    314       ]
    315     end
    316 
    317     @impl true
    318     def explain_query(conn, query, params, opts) do
    319       params = prepare_params(params)
    320 
    321       case Tds.query_multi(conn, build_explain_query(query), params, opts) do
    322         {:ok, [_, %Tds.Result{} = result, _]} ->
    323           {:ok, SQL.format_table(result)}
    324 
    325         error ->
    326           error
    327       end
    328     end
    329 
    330     def build_explain_query(query) do
    331       [
    332         "SET STATISTICS XML ON; ",
    333         "SET STATISTICS PROFILE ON; ",
    334         query,
    335         "; ",
    336         "SET STATISTICS XML OFF; ",
    337         "SET STATISTICS PROFILE OFF;"
    338       ]
    339       |> IO.iodata_to_binary()
    340     end
    341 
    342     ## Query generation
    343 
    344     binary_ops = [
    345       ==: " = ",
    346       !=: " <> ",
    347       <=: " <= ",
    348       >=: " >= ",
    349       <: " < ",
    350       >: " > ",
    351       +: " + ",
    352       -: " - ",
    353       *: " * ",
    354       /: " / ",
    355       and: " AND ",
    356       or: " OR ",
    357       ilike: " LIKE ",
    358       like: " LIKE "
    359     ]
    360 
    361     @binary_ops Keyword.keys(binary_ops)
    362 
    363     Enum.map(binary_ops, fn {op, str} ->
    364       defp handle_call(unquote(op), 2), do: {:binary_op, unquote(str)}
    365     end)
    366 
    367     defp handle_call(fun, _arity), do: {:fun, Atom.to_string(fun)}
    368 
    369     defp select(%{select: %{fields: fields}, distinct: distinct} = query, sources) do
    370       [
    371         "SELECT ",
    372         distinct(distinct, sources, query),
    373         limit(query, sources),
    374         select(fields, sources, query)
    375       ]
    376     end
    377 
    378     defp distinct(nil, _sources, _query), do: []
    379     defp distinct(%QueryExpr{expr: true}, _sources, _query), do: "DISTINCT "
    380     defp distinct(%QueryExpr{expr: false}, _sources, _query), do: []
    381 
    382     defp distinct(%QueryExpr{expr: exprs}, _sources, query) when is_list(exprs) do
    383       error!(
    384         query,
    385         "DISTINCT with multiple columns is not supported by MsSQL. " <>
    386           "Please use distinct(true) if you need distinct resultset"
    387       )
    388     end
    389 
    390     defp select([], _sources, _query) do
    391       "CAST(1 as bit)"
    392     end
    393 
    394     defp select(fields, sources, query) do
    395       intersperse_map(fields, ", ", fn
    396         {:&, _, [idx]} ->
    397           case elem(sources, idx) do
    398             {nil, source, nil} ->
    399               error!(query, "Tds adapter does not support selecting all fields from fragment #{source}. " <>
    400                             "Please specify exactly which fields you want to select")
    401 
    402             {source, _, nil} ->
    403               error!(query, "Tds adapter does not support selecting all fields from #{source} without a schema. " <>
    404                             "Please specify a schema or specify exactly which fields you want in projection")
    405 
    406             {_, source, _} ->
    407               source
    408           end
    409 
    410         {key, value} ->
    411           [select_expr(value, sources, query), " AS ", quote_name(key)]
    412 
    413         value ->
    414           select_expr(value, sources, query)
    415       end)
    416     end
    417 
    418     defp select_expr({:not, _, [expr]}, sources, query) do
    419       [?~, ?(, select_expr(expr, sources, query), ?)]
    420     end
    421 
    422     defp select_expr(value, sources, query), do: expr(value, sources, query)
    423 
    424     defp from(%{from: %{source: source, hints: hints}} = query, sources) do
    425       {from, name} = get_source(query, sources, 0, source)
    426 
    427       [" FROM ", from, " AS ", name, hints(hints)]
    428     end
    429 
    430     defp cte(%{with_ctes: %WithExpr{queries: [_ | _] = queries}} = query, sources) do
    431       ctes = intersperse_map(queries, ", ", &cte_expr(&1, sources, query))
    432       ["WITH ", ctes, " "]
    433     end
    434 
    435     defp cte(%{with_ctes: _}, _), do: []
    436 
    437     defp cte_expr({name, cte}, sources, query) do
    438       [quote_name(name), cte_header(cte, query), " AS ", cte_query(cte, sources, query)]
    439     end
    440 
    441     defp cte_header(%QueryExpr{}, query) do
    442       error!(
    443         query,
    444         "Tds adapter does not support fragment in CTE"
    445       )
    446     end
    447 
    448     defp cte_header(%Ecto.Query{select: %{fields: fields}} = query, _) do
    449       [
    450         " (",
    451         intersperse_map(fields, ",", fn
    452           {key, _} ->
    453             quote_name(key)
    454 
    455           other ->
    456             error!(
    457               query,
    458               "Tds adapter expected field name or alias in CTE header," <>
    459                 " instead got #{inspect(other)}"
    460             )
    461         end),
    462         ?)
    463       ]
    464     end
    465 
    466     defp cte_query(%Ecto.Query{} = query, sources, parent_query) do
    467       query = put_in(query.aliases[@parent_as], {parent_query, sources})
    468       [?(, all(query, subquery_as_prefix(sources)), ?)]
    469     end
    470 
    471     defp update_fields(%Query{updates: updates} = query, sources) do
    472       for(
    473         %{expr: expr} <- updates,
    474         {op, kw} <- expr,
    475         {key, value} <- kw,
    476         do: update_op(op, key, value, sources, query)
    477       )
    478       |> Enum.intersperse(", ")
    479     end
    480 
    481     defp update_op(:set, key, value, sources, query) do
    482       {_table, name, _model} = elem(sources, 0)
    483       [name, ?., quote_name(key), " = " | expr(value, sources, query)]
    484     end
    485 
    486     defp update_op(:inc, key, value, sources, query) do
    487       {_table, name, _model} = elem(sources, 0)
    488       quoted = quote_name(key)
    489 
    490       [name, ?., quoted, " = ", name, ?., quoted, " + " | expr(value, sources, query)]
    491     end
    492 
    493     defp update_op(command, _key, _value, _sources, query) do
    494       error!(query, "Unknown update operation #{inspect(command)} for TDS")
    495     end
    496 
    497     defp join(%{joins: []}, _sources), do: []
    498 
    499     defp join(%{joins: joins} = query, sources) do
    500       [
    501         ?\s,
    502         intersperse_map(joins, ?\s, fn
    503           %JoinExpr{on: %QueryExpr{expr: expr}, qual: qual, ix: ix, source: source, hints: hints} ->
    504             {join, name} = get_source(query, sources, ix, source)
    505             qual_text = join_qual(qual)
    506             join = join || ["(", expr(source, sources, query) | ")"]
    507             [qual_text, join, " AS ", name, hints(hints) | join_on(qual, expr, sources, query)]
    508         end)
    509       ]
    510     end
    511 
    512     defp join_on(:cross, true, _sources, _query), do: []
    513     defp join_on(:inner_lateral, true, _sources, _query), do: []
    514     defp join_on(:left_lateral, true, _sources, _query), do: []
    515     defp join_on(_qual, true, _sources, _query), do: [" ON 1 = 1"]
    516     defp join_on(_qual, expr, sources, query), do: [" ON " | expr(expr, sources, query)]
    517 
    518     defp join_qual(:inner), do: "INNER JOIN "
    519     defp join_qual(:inner_loop), do: "INNER LOOP JOIN "
    520     defp join_qual(:inner_hash), do: "INNER HASH JOIN "
    521     defp join_qual(:inner_merge), do: "INNER MERGE JOIN "
    522     defp join_qual(:inner_remote), do: "INNER REMOTE JOIN "
    523     defp join_qual(:left), do: "LEFT OUTER JOIN "
    524     defp join_qual(:right), do: "RIGHT OUTER JOIN "
    525     defp join_qual(:full), do: "FULL OUTER JOIN "
    526     defp join_qual(:cross), do: "CROSS JOIN "
    527     defp join_qual(:inner_lateral), do: "CROSS APPLY "
    528     defp join_qual(:left_lateral), do: "OUTER APPLY "
    529 
    530     defp where(%Query{wheres: wheres} = query, sources) do
    531       boolean(" WHERE ", wheres, sources, query)
    532     end
    533 
    534     defp having(%Query{havings: havings} = query, sources) do
    535       boolean(" HAVING ", havings, sources, query)
    536     end
    537 
    538     defp window(%{windows: []}, _sources), do: []
    539 
    540     defp window(_query, _sources),
    541       do: raise(RuntimeError, "Tds adapter does not support window functions")
    542 
    543     defp group_by(%{group_bys: []}, _sources), do: []
    544 
    545     defp group_by(%{group_bys: group_bys} = query, sources) do
    546       [
    547         " GROUP BY "
    548         | intersperse_map(group_bys, ", ", fn %QueryExpr{expr: expr} ->
    549             intersperse_map(expr, ", ", &expr(&1, sources, query))
    550           end)
    551       ]
    552     end
    553 
    554     defp order_by(%{order_bys: []}, _sources), do: []
    555 
    556     defp order_by(%{order_bys: order_bys} = query, sources) do
    557       [
    558         " ORDER BY "
    559         | intersperse_map(order_bys, ", ", fn %QueryExpr{expr: expr} ->
    560             intersperse_map(expr, ", ", &order_by_expr(&1, sources, query))
    561           end)
    562       ]
    563     end
    564 
    565     defp order_by_expr({dir, expr}, sources, query) do
    566       str = expr(expr, sources, query)
    567 
    568       case dir do
    569         :asc -> str
    570         :desc -> [str | " DESC"]
    571         _ -> error!(query, "#{dir} is not supported in ORDER BY in MSSQL")
    572       end
    573     end
    574 
    575     defp limit(%Query{limit: nil}, _sources), do: []
    576 
    577     defp limit(
    578            %Query{
    579              limit: %QueryExpr{
    580                expr: expr
    581              }
    582            } = query,
    583            sources
    584          ) do
    585       case Map.get(query, :offset) do
    586         nil ->
    587           ["TOP(", expr(expr, sources, query), ") "]
    588 
    589         _ ->
    590           []
    591       end
    592     end
    593 
    594     defp offset(%{offset: nil}, _sources), do: []
    595 
    596     defp offset(%Query{offset: _, limit: nil} = query, _sources) do
    597       error!(query, "You must provide a limit while using an offset")
    598     end
    599 
    600     defp offset(%{offset: offset, limit: limit} = query, sources) do
    601       [
    602         " OFFSET ",
    603         expr(offset.expr, sources, query),
    604         " ROW",
    605         " FETCH NEXT ",
    606         expr(limit.expr, sources, query),
    607         " ROWS ONLY"
    608       ]
    609     end
    610 
    611     defp hints([_ | _] = hints), do: [" WITH (", Enum.intersperse(hints, ", "), ?)]
    612     defp hints([]), do: []
    613 
    614     defp lock(%{lock: nil}, _sources), do: []
    615     defp lock(%{lock: binary}, _sources) when is_binary(binary), do: [" OPTION (", binary, ?)]
    616     defp lock(%{lock: expr} = query, sources), do: [" OPTION (", expr(expr, sources, query), ?)]
    617 
    618     defp combinations(%{combinations: combinations}) do
    619       Enum.map(combinations, fn
    620         {:union, query} -> [" UNION (", all(query), ")"]
    621         {:union_all, query} -> [" UNION ALL (", all(query), ")"]
    622         {:except, query} -> [" EXCEPT (", all(query), ")"]
    623         {:except_all, query} -> [" EXCEPT ALL (", all(query), ")"]
    624         {:intersect, query} -> [" INTERSECT (", all(query), ")"]
    625         {:intersect_all, query} -> [" INTERSECT ALL (", all(query), ")"]
    626       end)
    627     end
    628 
    629     defp boolean(_name, [], _sources, _query), do: []
    630 
    631     defp boolean(name, [%{expr: expr, op: op} | query_exprs], sources, query) do
    632       [
    633         name
    634         | Enum.reduce(query_exprs, {op, paren_expr(expr, sources, query)}, fn
    635             %BooleanExpr{expr: expr, op: op}, {op, acc} ->
    636               {op, [acc, operator_to_boolean(op), paren_expr(expr, sources, query)]}
    637 
    638             %BooleanExpr{expr: expr, op: op}, {_, acc} ->
    639               {op, [?(, acc, ?), operator_to_boolean(op), paren_expr(expr, sources, query)]}
    640           end)
    641           |> elem(1)
    642       ]
    643     end
    644 
    645     defp operator_to_boolean(:and), do: " AND "
    646     defp operator_to_boolean(:or), do: " OR "
    647 
    648     defp parens_for_select([first_expr | _] = expr) do
    649       if is_binary(first_expr) and String.match?(first_expr, ~r/^\s*select/i) do
    650         [?(, expr, ?)]
    651       else
    652         expr
    653       end
    654     end
    655 
    656     defp paren_expr(true, _sources, _query) do
    657       ["(1 = 1)"]
    658     end
    659 
    660     defp paren_expr(false, _sources, _query) do
    661       ["(1 = 0)"]
    662     end
    663 
    664     defp paren_expr(expr, sources, query) do
    665       [?(, expr(expr, sources, query), ?)]
    666     end
    667 
    668     # :^ - represents parameter ix is index number
    669     defp expr({:^, [], [idx]}, _sources, _query) do
    670       "@#{idx + 1}"
    671     end
    672 
    673     defp expr({{:., _, [{:parent_as, _, [as]}, field]}, _, []}, _sources, query)
    674          when is_atom(field) do
    675       {ix, sources} = get_parent_sources_ix(query, as)
    676       {_, name, _} = elem(sources, ix)
    677       [name, ?. | quote_name(field)]
    678     end
    679 
    680     defp expr({{:., _, [{:&, _, [idx]}, field]}, _, []}, sources, _query)
    681          when is_atom(field) or is_binary(field) do
    682       {_, name, _} = elem(sources, idx)
    683       [name, ?. | quote_name(field)]
    684     end
    685 
    686     defp expr({:&, _, [idx]}, sources, _query) do
    687       {_table, source, _schema} = elem(sources, idx)
    688       source
    689     end
    690 
    691     defp expr({:&, _, [idx, fields, _counter]}, sources, query) do
    692       {_table, name, schema} = elem(sources, idx)
    693 
    694       if is_nil(schema) and is_nil(fields) do
    695         error!(
    696           query,
    697           "Tds adapter requires a schema module when using selector #{inspect(name)} but " <>
    698             "none was given. Please specify schema " <>
    699             "or specify exactly which fields from #{inspect(name)} you what in projection"
    700         )
    701       end
    702 
    703       Enum.map_join(fields, ", ", &"#{name}.#{quote_name(&1)}")
    704     end
    705 
    706     # example from {:in, [], [1,   {:^, [], [0, 0]}]}
    707     defp expr({:in, _, [_left, []]}, _sources, _query) do
    708       "0=1"
    709     end
    710 
    711     # example from(p in Post, where: p.id in [1,2, ^some_id])
    712     defp expr({:in, _, [left, right]}, sources, query) when is_list(right) do
    713       args = Enum.map_join(right, ",", &expr(&1, sources, query))
    714       [expr(left, sources, query), " IN (", args | ")"]
    715     end
    716 
    717     # example from(p in Post, where: p.id in [])
    718     defp expr({:in, _, [_, {:^, _, [_, 0]}]}, _sources, _query), do: "0=1"
    719 
    720     # example from(p in Post, where: p.id in ^some_list)
    721     # or from(p in Post, where: p.id in ^[])
    722     defp expr({:in, _, [left, {:^, _, [idx, length]}]}, sources, query) do
    723       args = list_param_to_args(idx, length)
    724       [expr(left, sources, query), " IN (", args | ")"]
    725     end
    726 
    727     defp expr({:in, _, [left, %Ecto.SubQuery{} = subquery]}, sources, query) do
    728       [expr(left, sources, query), " IN ", expr(subquery, sources, query)]
    729     end
    730 
    731     defp expr({:in, _, [left, right]}, sources, query) do
    732       [expr(left, sources, query), " = ANY(", expr(right, sources, query) | ")"]
    733     end
    734 
    735     defp expr({:is_nil, _, [arg]}, sources, query) do
    736       "#{expr(arg, sources, query)} IS NULL"
    737     end
    738 
    739     defp expr({:not, _, [expr]}, sources, query) do
    740       ["NOT (", expr(expr, sources, query) | ")"]
    741     end
    742 
    743     defp expr({:filter, _, _}, _sources, query) do
    744       error!(query, "Tds adapter does not support aggregate filters")
    745     end
    746 
    747     defp expr(%Ecto.SubQuery{query: query}, sources, parent_query) do
    748       query = put_in(query.aliases[@parent_as], {parent_query, sources})
    749       [?(, all(query, subquery_as_prefix(sources)), ?)]
    750     end
    751 
    752     defp expr({:fragment, _, [kw]}, _sources, query) when is_list(kw) or tuple_size(kw) == 3 do
    753       error!(query, "Tds adapter does not support keyword or interpolated fragments")
    754     end
    755 
    756     defp expr({:fragment, _, parts}, sources, query) do
    757       Enum.map(parts, fn
    758         {:raw, part} -> part
    759         {:expr, expr} -> expr(expr, sources, query)
    760       end)
    761       |> parens_for_select
    762     end
    763 
    764     defp expr({:literal, _, [literal]}, _sources, _query) do
    765       quote_name(literal)
    766     end
    767 
    768     defp expr({:selected_as, _, [name]}, _sources, _query) do
    769       [quote_name(name)]
    770     end
    771 
    772     defp expr({:datetime_add, _, [datetime, count, interval]}, sources, query) do
    773       [
    774         "DATEADD(",
    775         interval,
    776         ", ",
    777         interval_count(count, sources, query),
    778         ", CAST(",
    779         expr(datetime, sources, query),
    780         " AS datetime2(6)))"
    781       ]
    782     end
    783 
    784     defp expr({:date_add, _, [date, count, interval]}, sources, query) do
    785       [
    786         "CAST(DATEADD(",
    787         interval,
    788         ", ",
    789         interval_count(count, sources, query),
    790         ", CAST(",
    791         expr(date, sources, query),
    792         " AS datetime2(6))" | ") AS date)"
    793       ]
    794     end
    795 
    796     defp expr({:count, _, []}, _sources, _query), do: "count(*)"
    797 
    798     defp expr({:json_extract_path, _, _}, _sources, query) do
    799       error!(
    800         query,
    801         "Tds adapter does not support json_extract_path expression" <>
    802           ", use fragment with JSON_VALUE/JSON_QUERY"
    803       )
    804     end
    805 
    806     defp expr({fun, _, args}, sources, query) when is_atom(fun) and is_list(args) do
    807       {modifier, args} =
    808         case args do
    809           [rest, :distinct] -> {"DISTINCT ", [rest]}
    810           _ -> {"", args}
    811         end
    812 
    813       case handle_call(fun, length(args)) do
    814         {:binary_op, op} ->
    815           [left, right] = args
    816           [op_to_binary(left, sources, query), op | op_to_binary(right, sources, query)]
    817 
    818         {:fun, fun} ->
    819           [fun, ?(, modifier, intersperse_map(args, ", ", &expr(&1, sources, query)), ?)]
    820       end
    821     end
    822 
    823     defp expr(list, sources, query) when is_list(list) do
    824       Enum.map_join(list, ", ", &expr(&1, sources, query))
    825     end
    826 
    827     defp expr({string, :varchar}, _sources, _query)
    828          when is_binary(string) do
    829       "'#{escape_string(string)}'"
    830     end
    831 
    832     defp expr(string, _sources, _query) when is_binary(string) do
    833       "N'#{escape_string(string)}'"
    834     end
    835 
    836     defp expr(%Decimal{exp: exp} = decimal, _sources, _query) do
    837       # this should help gaining precision for decimals values embedded in query
    838       # but this is still not good enough, for instance:
    839       #
    840       # from(p in Post, select: type(2.0 + ^"2", p.cost())))
    841       #
    842       # Post.cost is :decimal, but we don't know precision and scale since
    843       # such info is only available in migration files. So query compilation
    844       # will yield
    845       #
    846       # SELECT CAST(CAST(2.0 as decimal(38, 1)) + @1 AS decimal)
    847       # FROM [posts] AS p0
    848       #
    849       # as long as we have CAST(... as DECIMAL) without precision and scale
    850       # value could be truncated
    851       [
    852         "CAST(",
    853         Decimal.to_string(decimal, :normal),
    854         " as decimal(38, #{abs(exp)})",
    855         ?)
    856       ]
    857     end
    858 
    859     defp expr(%Tagged{value: binary, type: :binary}, _sources, _query) when is_binary(binary) do
    860       hex = Base.encode16(binary, case: :lower)
    861       "0x#{hex}"
    862     end
    863 
    864     defp expr(%Tagged{value: binary, type: :uuid}, _sources, _query) when is_binary(binary) do
    865       case binary do
    866         <<_::64, ?-, _::32, ?-, _::32, ?-, _::32, ?-, _::96>> ->
    867           {:ok, value} = Tds.Ecto.UUID.dump(binary)
    868           value
    869 
    870         any ->
    871           any
    872       end
    873     end
    874 
    875     defp expr(%Tagged{value: other, type: type}, sources, query)
    876          when type in [:varchar, :nvarchar] do
    877       "CAST(#{expr(other, sources, query)} AS #{column_type(type, [])}(max))"
    878     end
    879 
    880     defp expr(%Tagged{value: other, type: :integer}, sources, query) do
    881       "CAST(#{expr(other, sources, query)} AS bigint)"
    882     end
    883 
    884     defp expr(%Tagged{value: other, type: type}, sources, query) do
    885       "CAST(#{expr(other, sources, query)} AS #{column_type(type, [])})"
    886     end
    887 
    888     defp expr(nil, _sources, _query), do: "NULL"
    889     defp expr(true, _sources, _query), do: "1"
    890     defp expr(false, _sources, _query), do: "0"
    891 
    892     defp expr(literal, _sources, _query) when is_binary(literal) do
    893       "'#{escape_string(literal)}'"
    894     end
    895 
    896     defp expr(literal, _sources, _query) when is_integer(literal) do
    897       Integer.to_string(literal)
    898     end
    899 
    900     defp expr(literal, _sources, _query) when is_float(literal) do
    901       Float.to_string(literal)
    902     end
    903 
    904     defp expr(field, _sources, query) do
    905       error!(query, "unsupported MSSQL expressions: `#{inspect(field)}`")
    906     end
    907 
    908     defp op_to_binary({op, _, [_, _]} = expr, sources, query) when op in @binary_ops do
    909       paren_expr(expr, sources, query)
    910     end
    911 
    912     defp op_to_binary({:is_nil, _, [_]} = expr, sources, query) do
    913       paren_expr(expr, sources, query)
    914     end
    915 
    916     defp op_to_binary(expr, sources, query) do
    917       expr(expr, sources, query)
    918     end
    919 
    920     defp interval_count(count, _sources, _query) when is_integer(count) do
    921       Integer.to_string(count)
    922     end
    923 
    924     defp interval_count(count, _sources, _query) when is_float(count) do
    925       :erlang.float_to_binary(count, [:compact, decimals: 16])
    926     end
    927 
    928     defp interval_count(count, sources, query) do
    929       expr(count, sources, query)
    930     end
    931 
    932     defp returning([], _verb), do: []
    933 
    934     defp returning(returning, verb) when is_list(returning) do
    935       [" OUTPUT ", intersperse_map(returning, ", ", &[verb, ?., quote_name(&1)])]
    936     end
    937 
    938     defp returning(%{select: nil}, _, _),
    939       do: []
    940 
    941     defp returning(%{select: %{fields: fields}} = query, idx, verb),
    942       do: [
    943         " OUTPUT "
    944         | intersperse_map(fields, ", ", fn
    945             {{:., _, [{:&, _, [^idx]}, key]}, _, _} -> [verb, ?., quote_name(key)]
    946             _ -> error!(query, "MSSQL can only return table #{verb} columns")
    947           end)
    948       ]
    949 
    950     defp create_names(%{sources: sources}, as_prefix) do
    951       create_names(sources, 0, tuple_size(sources), as_prefix) |> List.to_tuple()
    952     end
    953 
    954     defp create_names(sources, pos, limit, as_prefix) when pos < limit do
    955       [create_name(sources, pos, as_prefix) | create_names(sources, pos + 1, limit, as_prefix)]
    956     end
    957 
    958     defp create_names(_sources, pos, pos, as_prefix) do
    959       [as_prefix]
    960     end
    961 
    962     defp subquery_as_prefix(sources) do
    963       [?s | :erlang.element(tuple_size(sources), sources)]
    964     end
    965 
    966     defp create_name(sources, pos, as_prefix) do
    967       case elem(sources, pos) do
    968         {:fragment, _, _} ->
    969           {nil, as_prefix ++ [?f | Integer.to_string(pos)], nil}
    970 
    971         {table, model, prefix} ->
    972           name = as_prefix ++ [create_alias(table) | Integer.to_string(pos)]
    973           {quote_table(prefix, table), name, model}
    974 
    975         %Ecto.SubQuery{} ->
    976           {nil, as_prefix ++ [?s | Integer.to_string(pos)], nil}
    977       end
    978     end
    979 
    980     defp create_alias(<<first, _rest::binary>>) when first in ?a..?z when first in ?A..?Z do
    981       first
    982     end
    983 
    984     defp create_alias(_) do
    985       ?t
    986     end
    987 
    988     # DDL
    989     alias Ecto.Migration.{Table, Index, Reference, Constraint}
    990 
    991     @creates [:create, :create_if_not_exists]
    992     @drops [:drop, :drop_if_exists]
    993 
    994     @impl true
    995     def execute_ddl({command, %Table{} = table, columns}) when command in @creates do
    996       prefix = table.prefix
    997 
    998       pk_name =
    999         if table.prefix,
   1000           do: "#{table.prefix}_#{table.name}",
   1001           else: table.name
   1002 
   1003       table_structure =
   1004         table
   1005         |> column_definitions(columns)
   1006         |> Kernel.++(pk_definitions(columns, ", CONSTRAINT [#{pk_name}_pkey] "))
   1007         |> case do
   1008           [] -> []
   1009           list -> [" (", list, ?)]
   1010         end
   1011 
   1012       create_if_not_exists =
   1013         if_table_not_exists(command == :create_if_not_exists, table.name, prefix)
   1014 
   1015       [
   1016         [
   1017           create_if_not_exists,
   1018           "CREATE TABLE ",
   1019           quote_table(prefix, table.name),
   1020           table_structure,
   1021           engine_expr(table.engine),
   1022           options_expr(table.options),
   1023           "; "
   1024         ]
   1025       ]
   1026     end
   1027 
   1028     def execute_ddl({command, %Table{}, :cascade}) when command in @drops,
   1029       do: error!(nil, "MSSQL does not support `CASCADE` in DROP TABLE commands")
   1030 
   1031     def execute_ddl({command, %Table{} = table, :restrict}) when command in @drops do
   1032       prefix = table.prefix
   1033 
   1034       [
   1035         [
   1036           if_table_exists(command == :drop_if_exists, table.name, prefix),
   1037           "DROP TABLE ",
   1038           quote_table(prefix, table.name),
   1039           "; "
   1040         ]
   1041       ]
   1042     end
   1043 
   1044     def execute_ddl({:alter, %Table{} = table, changes}) do
   1045       statement_prefix = ["ALTER TABLE ", quote_table(table.prefix, table.name), " "]
   1046 
   1047       pk_name =
   1048         if table.prefix,
   1049           do: "#{table.prefix}_#{table.name}",
   1050           else: table.name
   1051 
   1052       pkeys =
   1053         case pk_definitions(changes, " CONSTRAINT [#{pk_name}_pkey] ") do
   1054           [] -> []
   1055           sql -> [statement_prefix, "ADD", sql]
   1056         end
   1057 
   1058       [
   1059         [
   1060           column_changes(statement_prefix, table, changes),
   1061           pkeys
   1062         ]
   1063       ]
   1064     end
   1065 
   1066     def execute_ddl({command, %Index{} = index}) when command in @creates do
   1067       prefix = index.prefix
   1068 
   1069       if index.using do
   1070         error!(nil, "MSSQL does not support `using` in indexes")
   1071       end
   1072 
   1073       if index.nulls_distinct == true do
   1074         error!(nil, "MSSQL does not support nulls_distinct set to true in indexes")
   1075       end
   1076 
   1077       with_options =
   1078         if index.concurrently or index.options != nil do
   1079           [
   1080             " WITH",
   1081             ?(,
   1082             if_do(index.concurrently, "ONLINE=ON"),
   1083             if_do(index.concurrently and index.options != nil, ","),
   1084             if_do(index.options != nil, index.options),
   1085             ?)
   1086           ]
   1087         else
   1088           []
   1089         end
   1090 
   1091       include =
   1092         index.include
   1093         |> List.wrap()
   1094         |> intersperse_map(", ", &index_expr/1)
   1095 
   1096       [
   1097         [
   1098           if_index_not_exists(
   1099             command == :create_if_not_exists,
   1100             index.name,
   1101             unquoted_name(prefix, index.table)
   1102           ),
   1103           "CREATE",
   1104           if_do(index.unique, " UNIQUE"),
   1105           " INDEX ",
   1106           quote_name(index.name),
   1107           " ON ",
   1108           quote_table(prefix, index.table),
   1109           " (",
   1110           intersperse_map(index.columns, ", ", &index_expr/1),
   1111           ?),
   1112           if_do(include != [], [" INCLUDE ", ?(, include, ?)]),
   1113           if_do(index.where, [" WHERE (", index.where, ?)]),
   1114           with_options,
   1115           ?;
   1116         ]
   1117       ]
   1118     end
   1119 
   1120     def execute_ddl({:create, %Constraint{exclude: exclude}}) when exclude != nil do
   1121       msg =
   1122         "`:exclude` is not supported Tds adapter check constraint parameter, instead " <>
   1123           "set `:check` attribute with negated expression."
   1124 
   1125       error!(nil, msg)
   1126     end
   1127 
   1128     def execute_ddl({:create, %Constraint{validate: false}}) do
   1129       error!(nil, "`:validate` is not supported by the Tds adapter")
   1130     end
   1131 
   1132     def execute_ddl({:create, %Constraint{} = constraint}) do
   1133       table_name = quote_table(constraint.prefix, constraint.table)
   1134 
   1135       [
   1136         [
   1137           "ALTER TABLE ",
   1138           table_name,
   1139           " ADD CONSTRAINT ",
   1140           quote_name(constraint.name),
   1141           " ",
   1142           "CHECK (",
   1143           constraint.check,
   1144           "); "
   1145         ]
   1146       ]
   1147     end
   1148 
   1149     def execute_ddl({command, %Index{}, :cascade}) when command in @drops,
   1150       do: error!(nil, "MSSQL does not support `CASCADE` in DROP INDEX commands")
   1151 
   1152     def execute_ddl({command, %Index{} = index, :restrict}) when command in @drops do
   1153       prefix = index.prefix
   1154 
   1155       [
   1156         [
   1157           if_index_exists(
   1158             command == :drop_if_exists,
   1159             index.name,
   1160             unquoted_name(prefix, index.table)
   1161           ),
   1162           "DROP INDEX ",
   1163           quote_name(index.name),
   1164           " ON ",
   1165           quote_table(prefix, index.table),
   1166           if_do(index.concurrently, " LOCK=NONE"),
   1167           "; "
   1168         ]
   1169       ]
   1170     end
   1171 
   1172     def execute_ddl({command, %Constraint{}, :cascade}) when command in @drops,
   1173       do: error!(nil, "MSSQL does not support `CASCADE` in DROP CONSTRAINT commands")
   1174 
   1175     def execute_ddl({command, %Constraint{} = constraint, _}) when command in @drops do
   1176       table_name = quote_table(constraint.prefix, constraint.table)
   1177 
   1178       [
   1179         [
   1180           if_check_constraint_exists(
   1181             command == :drop_if_exists,
   1182             constraint.name,
   1183             constraint.prefix
   1184           ),
   1185           "ALTER TABLE ",
   1186           table_name,
   1187           " DROP CONSTRAINT ",
   1188           quote_name(constraint.name),
   1189           "; "
   1190         ]
   1191       ]
   1192     end
   1193 
   1194     def execute_ddl({:rename, %Table{} = current_table, %Table{} = new_table}) do
   1195       [
   1196         [
   1197           "EXEC sp_rename '",
   1198           unquoted_name(current_table.prefix, current_table.name),
   1199           "', '",
   1200           unquoted_name(new_table.prefix, new_table.name),
   1201           "'"
   1202         ]
   1203       ]
   1204     end
   1205 
   1206     def execute_ddl({:rename, table, current_column, new_column}) do
   1207       [
   1208         [
   1209           "EXEC sp_rename '",
   1210           unquoted_name(table.prefix, table.name, current_column),
   1211           "', '",
   1212           unquoted_name(new_column),
   1213           "', 'COLUMN'"
   1214         ]
   1215       ]
   1216     end
   1217 
   1218     def execute_ddl(string) when is_binary(string), do: [string]
   1219 
   1220     def execute_ddl(keyword) when is_list(keyword),
   1221       do: error!(nil, "Tds adapter does not support keyword lists in execute")
   1222 
   1223     @impl true
   1224     def ddl_logs(_), do: []
   1225 
   1226     @impl true
   1227     def table_exists_query(table) do
   1228       {"SELECT 1 FROM sys.tables WHERE [name] = @1", [table]}
   1229     end
   1230 
   1231     defp pk_definitions(columns, prefix) do
   1232       pks =
   1233         for {_, name, _, opts} <- columns,
   1234             opts[:primary_key],
   1235             do: name
   1236 
   1237       case pks do
   1238         [] ->
   1239           []
   1240 
   1241         _ ->
   1242           [prefix, "PRIMARY KEY CLUSTERED (", quote_names(pks), ?)]
   1243       end
   1244     end
   1245 
   1246     defp column_definitions(table, columns) do
   1247       intersperse_map(columns, ", ", &column_definition(table, &1))
   1248     end
   1249 
   1250     defp column_definition(table, {:add, name, %Reference{} = ref, opts}) do
   1251       [
   1252         quote_name(name),
   1253         " ",
   1254         reference_column_type(ref.type, opts),
   1255         column_options(table, name, opts),
   1256         reference_expr(ref, table, name)
   1257       ]
   1258     end
   1259 
   1260     defp column_definition(table, {:add, name, type, opts}) do
   1261       [quote_name(name), " ", column_type(type, opts), column_options(table, name, opts)]
   1262     end
   1263 
   1264     defp column_changes(statement, table, columns) do
   1265       for column <- columns do
   1266         column_change(statement, table, column)
   1267       end
   1268     end
   1269 
   1270     defp column_change(_statement_prefix, _table, {_command, _name, %Reference{validate: false}, _opts}) do
   1271       error!(nil, "validate: false on references is not supported in Tds")
   1272     end
   1273 
   1274     defp column_change(statement_prefix, table, {:add, name, %Reference{} = ref, opts}) do
   1275       [
   1276         [
   1277           statement_prefix,
   1278           "ADD ",
   1279           quote_name(name),
   1280           " ",
   1281           reference_column_type(ref.type, opts),
   1282           column_options(table, name, opts),
   1283           "; "
   1284         ],
   1285         [statement_prefix, "ADD", constraint_expr(ref, table, name), "; "]
   1286       ]
   1287     end
   1288 
   1289     defp column_change(statement_prefix, table, {:add, name, type, opts}) do
   1290       [
   1291         [
   1292           statement_prefix,
   1293           "ADD ",
   1294           quote_name(name),
   1295           " ",
   1296           column_type(type, opts),
   1297           column_options(table, name, opts),
   1298           "; "
   1299         ]
   1300       ]
   1301     end
   1302 
   1303     defp column_change(
   1304            statement_prefix,
   1305            %{name: table_name, prefix: prefix} = table,
   1306            {:add_if_not_exists, column_name, type, opts}
   1307          ) do
   1308       [
   1309         [
   1310           if_column_not_exists(prefix, table_name, column_name),
   1311           statement_prefix,
   1312           "ADD ",
   1313           quote_name(column_name),
   1314           " ",
   1315           column_type(type, opts),
   1316           column_options(table, column_name, opts),
   1317           "; "
   1318         ]
   1319       ]
   1320     end
   1321 
   1322     defp column_change(statement_prefix, table, {:modify, name, %Reference{} = ref, opts}) do
   1323       [
   1324         drop_constraint_from_expr(opts[:from], table, name, statement_prefix),
   1325         maybe_drop_default_expr(statement_prefix, table, name, opts),
   1326         [
   1327           statement_prefix,
   1328           "ALTER COLUMN ",
   1329           quote_name(name),
   1330           " ",
   1331           reference_column_type(ref.type, opts),
   1332           column_options(table, name, opts),
   1333           "; "
   1334         ],
   1335         [statement_prefix, "ADD", constraint_expr(ref, table, name), "; "],
   1336         [column_default_value(statement_prefix, table, name, opts)]
   1337       ]
   1338     end
   1339 
   1340     defp column_change(statement_prefix, table, {:modify, name, type, opts}) do
   1341       [
   1342         drop_constraint_from_expr(opts[:from], table, name, statement_prefix),
   1343         maybe_drop_default_expr(statement_prefix, table, name, opts),
   1344         [
   1345           statement_prefix,
   1346           "ALTER COLUMN ",
   1347           quote_name(name),
   1348           " ",
   1349           column_type(type, opts),
   1350           null_expr(Keyword.get(opts, :null)),
   1351           "; "
   1352         ],
   1353         [column_default_value(statement_prefix, table, name, opts)]
   1354       ]
   1355     end
   1356 
   1357     defp column_change(statement_prefix, _table, {:remove, name}) do
   1358       [statement_prefix, "DROP COLUMN ", quote_name(name), "; "]
   1359     end
   1360 
   1361     defp column_change(
   1362            statement_prefix,
   1363            %{name: table, prefix: prefix},
   1364            {:remove_if_exists, column_name, _}
   1365          ) do
   1366       [
   1367         [
   1368           if_column_exists(prefix, table, column_name),
   1369           statement_prefix,
   1370           "DROP COLUMN ",
   1371           quote_name(column_name),
   1372           "; "
   1373         ]
   1374       ]
   1375     end
   1376 
   1377     defp column_options(table, name, opts) do
   1378       default = Keyword.fetch(opts, :default)
   1379       null = Keyword.get(opts, :null)
   1380       [null_expr(null), default_expr(table, name, default)]
   1381     end
   1382 
   1383     defp column_default_value(statement_prefix, table, name, opts) do
   1384       default_expression = default_expr(table, name, Keyword.fetch(opts, :default))
   1385 
   1386       case default_expression do
   1387         [] -> []
   1388         _ -> [statement_prefix, "ADD", default_expression, " FOR ", quote_name(name), "; "]
   1389       end
   1390     end
   1391 
   1392     defp null_expr(false), do: [" NOT NULL"]
   1393     defp null_expr(true), do: [" NULL"]
   1394     defp null_expr(_), do: []
   1395 
   1396     defp default_expr(_table, _name, {:ok, nil}),
   1397       do: []
   1398 
   1399     defp default_expr(table, name, {:ok, literal}) when is_binary(literal),
   1400       do: [
   1401         " CONSTRAINT ",
   1402         constraint_name("DF", table, name),
   1403         " DEFAULT (N'",
   1404         escape_string(literal),
   1405         "')"
   1406       ]
   1407 
   1408     defp default_expr(table, name, {:ok, true}),
   1409       do: [" CONSTRAINT ", constraint_name("DF", table, name), " DEFAULT (1)"]
   1410 
   1411     defp default_expr(table, name, {:ok, false}),
   1412       do: [" CONSTRAINT ", constraint_name("DF", table, name), " DEFAULT (0)"]
   1413 
   1414     defp default_expr(table, name, {:ok, literal}) when is_number(literal),
   1415       do: [
   1416         " CONSTRAINT ",
   1417         constraint_name("DF", table, name),
   1418         " DEFAULT (",
   1419         to_string(literal),
   1420         ")"
   1421       ]
   1422 
   1423     defp default_expr(table, name, {:ok, {:fragment, expr}}),
   1424       do: [" CONSTRAINT ", constraint_name("DF", table, name), " DEFAULT (", expr, ")"]
   1425 
   1426     defp default_expr(_table, _name, :error), do: []
   1427 
   1428     defp drop_constraint_from_expr(%Reference{} = ref, table, name, stm_prefix) do
   1429       [stm_prefix, "DROP CONSTRAINT ", reference_name(ref, table, name), "; "]
   1430     end
   1431 
   1432     defp drop_constraint_from_expr(_, _, _, _),
   1433       do: []
   1434 
   1435     defp maybe_drop_default_expr(statement_prefix, table, name, opts) do
   1436       if Keyword.has_key?(opts, :default) do
   1437         constraint_name = constraint_name("DF", table, name)
   1438         if_exists_drop_constraint(constraint_name, statement_prefix)
   1439       else
   1440         []
   1441       end
   1442     end
   1443 
   1444     defp constraint_name(type, table, name),
   1445       do: quote_name("#{type}_#{table.prefix}_#{table.name}_#{name}")
   1446 
   1447     defp index_expr(literal) when is_binary(literal), do: literal
   1448     defp index_expr(literal), do: quote_name(literal)
   1449 
   1450     defp engine_expr(_storage_engine), do: [""]
   1451 
   1452     defp options_expr(nil), do: []
   1453 
   1454     defp options_expr(keyword) when is_list(keyword),
   1455       do: error!(nil, "Tds adapter does not support keyword lists in :options")
   1456 
   1457     defp options_expr(options), do: [" ", to_string(options)]
   1458 
   1459     defp column_type(type, opts) do
   1460       size = Keyword.get(opts, :size)
   1461       precision = Keyword.get(opts, :precision)
   1462       scale = Keyword.get(opts, :scale)
   1463       ecto_to_db(type, size, precision, scale)
   1464     end
   1465 
   1466     defp constraint_expr(%Reference{} = ref, table, name) do
   1467       {current_columns, reference_columns} = Enum.unzip([{name, ref.column} | ref.with])
   1468 
   1469       if ref.match do
   1470         error!(nil, ":match is not supported in references for tds")
   1471       end
   1472 
   1473       [
   1474         " CONSTRAINT ",
   1475         reference_name(ref, table, name),
   1476         " FOREIGN KEY (#{quote_names(current_columns)})",
   1477         " REFERENCES ",
   1478         quote_table(ref.prefix || table.prefix, ref.table),
   1479         "(#{quote_names(reference_columns)})",
   1480         reference_on_delete(ref.on_delete),
   1481         reference_on_update(ref.on_update)
   1482       ]
   1483     end
   1484 
   1485     defp reference_expr(%Reference{} = ref, table, name) do
   1486       [",", constraint_expr(ref, table, name)]
   1487     end
   1488 
   1489     defp reference_name(%Reference{name: nil}, table, column),
   1490       do: quote_name("#{table.name}_#{column}_fkey")
   1491 
   1492     defp reference_name(%Reference{name: name}, _table, _column), do: quote_name(name)
   1493 
   1494     defp reference_column_type(:id, _opts), do: "BIGINT"
   1495     defp reference_column_type(:serial, _opts), do: "INT"
   1496     defp reference_column_type(:bigserial, _opts), do: "BIGINT"
   1497     defp reference_column_type(type, opts), do: column_type(type, opts)
   1498 
   1499     defp reference_on_delete(:nilify_all), do: " ON DELETE SET NULL"
   1500     defp reference_on_delete(:delete_all), do: " ON DELETE CASCADE"
   1501     defp reference_on_delete(:nothing), do: " ON DELETE NO ACTION"
   1502     defp reference_on_delete(_), do: []
   1503 
   1504     defp reference_on_update(:nilify_all), do: " ON UPDATE SET NULL"
   1505     defp reference_on_update(:update_all), do: " ON UPDATE CASCADE"
   1506     defp reference_on_update(:nothing), do: " ON UPDATE NO ACTION"
   1507     defp reference_on_update(_), do: []
   1508 
   1509     ## Helpers
   1510 
   1511     defp get_source(query, sources, ix, source) do
   1512       {expr, name, _schema} = elem(sources, ix)
   1513       {expr || expr(source, sources, query), name}
   1514     end
   1515 
   1516     defp get_parent_sources_ix(query, as) do
   1517       case query.aliases[@parent_as] do
   1518         {%{aliases: %{^as => ix}}, sources} -> {ix, sources}
   1519         {%{} = parent, _sources} -> get_parent_sources_ix(parent, as)
   1520       end
   1521     end
   1522 
   1523     defp quote_name(name) when is_atom(name) do
   1524       quote_name(Atom.to_string(name))
   1525     end
   1526 
   1527     defp quote_name(name) when is_binary(name) do
   1528       if String.contains?(name, ["[", "]"]) do
   1529         error!(nil, "bad literal/field/table name #{inspect(name)} ('[' and ']' are not permitted)")
   1530       end
   1531 
   1532       "[#{name}]"
   1533     end
   1534 
   1535     defp quote_names(names), do: intersperse_map(names, ?,, &quote_name/1)
   1536 
   1537     defp quote_table(nil, name), do: quote_table(name)
   1538 
   1539     defp quote_table({server, db, schema}, name),
   1540       do: [quote_table(server), ".", quote_table(db), ".", quote_table(schema), ".", quote_table(name)]
   1541 
   1542     defp quote_table({db, schema}, name),
   1543       do: [quote_table(db), ".", quote_table(schema), ".", quote_table(name)]
   1544 
   1545     defp quote_table(prefix, name),
   1546       do: [quote_table(prefix), ".", quote_table(name)]
   1547 
   1548     defp quote_table(name) when is_atom(name), do: quote_table(Atom.to_string(name))
   1549 
   1550     defp quote_table(name) do
   1551       if String.contains?(name, "[") or String.contains?(name, "]") do
   1552         error!(nil, "bad table name #{inspect(name)} '[' and ']' are not permitted")
   1553       end
   1554 
   1555       "[#{name}]"
   1556     end
   1557 
   1558     defp unquoted_name(prefix, name, column_name),
   1559       do: unquoted_name(unquoted_name(prefix, name), column_name)
   1560 
   1561     defp unquoted_name(nil, name), do: unquoted_name(name)
   1562 
   1563     defp unquoted_name(prefix, name) do
   1564       prefix = if is_atom(prefix), do: Atom.to_string(prefix), else: prefix
   1565       name = if is_atom(name), do: Atom.to_string(name), else: name
   1566 
   1567       [prefix, ".", name]
   1568     end
   1569 
   1570     defp unquoted_name(name) when is_atom(name), do: unquoted_name(Atom.to_string(name))
   1571 
   1572     defp unquoted_name(name) do
   1573       if String.contains?(name, ["[", "]"]) do
   1574         error!(nil, "bad table name #{inspect(name)} '[' and ']' are not permitted")
   1575       end
   1576 
   1577       name
   1578     end
   1579 
   1580     defp intersperse_map([], _separator, _mapper), do: []
   1581     defp intersperse_map([elem], _separator, mapper), do: mapper.(elem)
   1582 
   1583     defp intersperse_map([elem | rest], separator, mapper) do
   1584       [mapper.(elem), separator | intersperse_map(rest, separator, mapper)]
   1585     end
   1586 
   1587     defp intersperse_reduce(list, separator, user_acc, reducer, acc \\ [])
   1588 
   1589     defp intersperse_reduce([], _separator, user_acc, _reducer, acc),
   1590       do: {acc, user_acc}
   1591 
   1592     defp intersperse_reduce([elem], _separator, user_acc, reducer, acc) do
   1593       {elem, user_acc} = reducer.(elem, user_acc)
   1594       {[acc | elem], user_acc}
   1595     end
   1596 
   1597     defp intersperse_reduce([elem | rest], separator, user_acc, reducer, acc) do
   1598       {elem, user_acc} = reducer.(elem, user_acc)
   1599       intersperse_reduce(rest, separator, user_acc, reducer, [acc, elem, separator])
   1600     end
   1601 
   1602     defp if_do(condition, value) do
   1603       if condition, do: value, else: []
   1604     end
   1605 
   1606     defp escape_string(value) when is_binary(value) do
   1607       value |> :binary.replace("'", "''", [:global])
   1608     end
   1609 
   1610     defp ecto_to_db(type, size, precision, scale, query \\ nil)
   1611 
   1612     defp ecto_to_db({:array, _}, _, _, _, query),
   1613       do: error!(query, "Array type is not supported by TDS")
   1614 
   1615     defp ecto_to_db(:id, _, _, _, _), do: "bigint"
   1616     defp ecto_to_db(:serial, _, _, _, _), do: "int IDENTITY(1,1)"
   1617     defp ecto_to_db(:bigserial, _, _, _, _), do: "bigint IDENTITY(1,1)"
   1618     defp ecto_to_db(:binary_id, _, _, _, _), do: "uniqueidentifier"
   1619     defp ecto_to_db(:boolean, _, _, _, _), do: "bit"
   1620     defp ecto_to_db(:string, nil, _, _, _), do: "nvarchar(255)"
   1621     defp ecto_to_db(:string, :max, _, _, _), do: "nvarchar(max)"
   1622     defp ecto_to_db(:string, s, _, _, _) when s in 1..4_000, do: "nvarchar(#{s})"
   1623     defp ecto_to_db(:float, nil, _, _, _), do: "float"
   1624     defp ecto_to_db(:float, s, _, _, _) when s in 1..53, do: "float(#{s})"
   1625     defp ecto_to_db(:binary, nil, _, _, _), do: "varbinary(max)"
   1626     defp ecto_to_db(:binary, s, _, _, _) when s in 1..8_000, do: "varbinary(#{s})"
   1627     defp ecto_to_db(:uuid, _, _, _, _), do: "uniqueidentifier"
   1628     defp ecto_to_db(:map, nil, _, _, _), do: "nvarchar(max)"
   1629     defp ecto_to_db(:map, s, _, _, _) when s in 0..4_000, do: "nvarchar(#{s})"
   1630     defp ecto_to_db({:map, _}, nil, _, _, _), do: "nvarchar(max)"
   1631     defp ecto_to_db({:map, _}, s, _, _, _) when s in 1..4_000, do: "nvarchar(#{s})"
   1632     defp ecto_to_db(:time, _, _, _, _), do: "time(0)"
   1633     defp ecto_to_db(:time_usec, _, p, _, _) when p in 0..7, do: "time(#{p})"
   1634     defp ecto_to_db(:time_usec, _, _, _, _), do: "time(6)"
   1635     defp ecto_to_db(:utc_datetime, _, _, _, _), do: "datetime"
   1636     defp ecto_to_db(:utc_datetime_usec, _, p, _, _) when p in 0..7, do: "datetime2(#{p})"
   1637     defp ecto_to_db(:utc_datetime_usec, _, _, _, _), do: "datetime2(6)"
   1638     defp ecto_to_db(:naive_datetime, _, _, _, _), do: "datetime"
   1639     defp ecto_to_db(:naive_datetime_usec, _, p, _, _) when p in 0..7, do: "datetime2(#{p})"
   1640     defp ecto_to_db(:naive_datetime_usec, _, _, _, _), do: "datetime2(6)"
   1641 
   1642     defp ecto_to_db(other, size, _, _, _) when is_integer(size) do
   1643       "#{Atom.to_string(other)}(#{size})"
   1644     end
   1645 
   1646     defp ecto_to_db(other, _, precision, scale, _) when is_integer(precision) do
   1647       "#{Atom.to_string(other)}(#{precision},#{scale || 0})"
   1648     end
   1649 
   1650     defp ecto_to_db(atom, nil, nil, nil, _) when is_atom(atom) do
   1651       Atom.to_string(atom)
   1652     end
   1653 
   1654     defp ecto_to_db(type, _, _, _, _) do
   1655       raise ArgumentError,
   1656             "unsupported type `#{inspect(type)}`. The type can either be an atom, a string " <>
   1657               "or a tuple of the form `{:map, t}` where `t` itself follows the same conditions."
   1658     end
   1659 
   1660     defp error!(nil, message) do
   1661       raise ArgumentError, message
   1662     end
   1663 
   1664     defp error!(query, message) do
   1665       raise Ecto.QueryError, query: query, message: message
   1666     end
   1667 
   1668     defp if_table_not_exists(condition, name, prefix) do
   1669       if_do(condition, [
   1670         "IF NOT EXISTS (SELECT * FROM [INFORMATION_SCHEMA].[TABLES] ",
   1671         "WHERE ",
   1672         "[TABLE_NAME] = ",
   1673         ?',
   1674         "#{name}",
   1675         ?',
   1676         if_do(prefix != nil, [
   1677           " AND [TABLE_SCHEMA] = ",
   1678           ?',
   1679           "#{prefix}",
   1680           ?'
   1681         ]),
   1682         ") "
   1683       ])
   1684     end
   1685 
   1686     defp if_table_exists(condition, name, prefix) do
   1687       if_do(condition, [
   1688         "IF EXISTS (SELECT * FROM [INFORMATION_SCHEMA].[TABLES] ",
   1689         "WHERE ",
   1690         "[TABLE_NAME] = ",
   1691         ?',
   1692         "#{name}",
   1693         ?',
   1694         if_do(prefix != nil, [
   1695           " AND [TABLE_SCHEMA] = ",
   1696           ?',
   1697           "#{prefix}",
   1698           ?'
   1699         ]),
   1700         ") "
   1701       ])
   1702     end
   1703 
   1704     defp if_column_exists(prefix, table, column_name) do
   1705       [
   1706         "IF EXISTS (SELECT 1 FROM [sys].[columns] ",
   1707         "WHERE [name] = N'#{column_name}'  AND ",
   1708         "[object_id] = OBJECT_ID(N'",
   1709         if_do(prefix != nil, ["#{prefix}", ?.]),
   1710         "#{table}",
   1711         "')) "
   1712       ]
   1713     end
   1714 
   1715     defp if_column_not_exists(prefix, table, column_name) do
   1716       [
   1717         "IF NOT EXISTS (SELECT 1 FROM [sys].[columns] ",
   1718         "WHERE [name] = N'#{column_name}' AND ",
   1719         "[object_id] = OBJECT_ID(N'",
   1720         if_do(prefix != nil, ["#{prefix}", ?.]),
   1721         "#{table}",
   1722         "')) "
   1723       ]
   1724     end
   1725 
   1726     defp list_param_to_args(idx, length) do
   1727       Enum.map_join(1..length, ",", &"@#{idx + &1}")
   1728     end
   1729 
   1730     defp as_string(atom) when is_atom(atom), do: Atom.to_string(atom)
   1731     defp as_string(str), do: str
   1732 
   1733     defp if_index_exists(condition, index_name, table_name) do
   1734       if_do(condition, [
   1735         "IF EXISTS (SELECT name FROM sys.indexes WHERE name = N'",
   1736         as_string(index_name),
   1737         "' AND object_id = OBJECT_ID(N'",
   1738         as_string(table_name),
   1739         "')) "
   1740       ])
   1741     end
   1742 
   1743     defp if_index_not_exists(condition, index_name, table_name) do
   1744       if_do(condition, [
   1745         "IF NOT EXISTS (SELECT name FROM sys.indexes WHERE name = N'",
   1746         as_string(index_name),
   1747         "' AND object_id = OBJECT_ID(N'",
   1748         as_string(table_name),
   1749         "')) "
   1750       ])
   1751     end
   1752 
   1753     defp if_check_constraint_exists(condition, name, prefix) do
   1754       if_do(condition, [
   1755         "IF NOT EXISTS (SELECT * ",
   1756         "FROM [INFORMATION_SCHEMA].[CHECK_CONSTRAINTS] ",
   1757         "WHERE [CONSTRAINT_NAME] = N'#{name}'",
   1758         if_do(prefix != nil, [
   1759           " AND [CONSTRAINT_SCHEMA] = N'#{prefix}'"
   1760         ]),
   1761         ") "
   1762       ])
   1763     end
   1764 
   1765     # types
   1766     # "U" - table,
   1767     # "C", "PK", "UQ", "F ", "D " - constraints
   1768     defp if_object_exists(name, type, statement) do
   1769       [
   1770         "IF (OBJECT_ID(N'",
   1771         name,
   1772         "', '",
   1773         type,
   1774         "') IS NOT NULL) ",
   1775         statement
   1776       ]
   1777     end
   1778 
   1779     defp if_exists_drop_constraint(name, statement_prefix) do
   1780       [
   1781         if_object_exists(
   1782           name,
   1783           "D",
   1784           "#{statement_prefix}DROP CONSTRAINT #{name}; "
   1785         )
   1786       ]
   1787     end
   1788   end
   1789 end