Skip to content

Commit 11ec77c

Browse files
committed
Add docs to SQL.Adapter default implementations
1 parent 6130b05 commit 11ec77c

1 file changed

Lines changed: 182 additions & 22 deletions

File tree

lib/ecto/adapters/sql.ex

Lines changed: 182 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -771,69 +771,229 @@ defmodule Ecto.Adapters.SQL do
771771
## Callbacks
772772

773773
@doc false
774-
def __before_compile__(_driver, _env) do
774+
def __before_compile__(driver, _env) do
775775
quote do
776776
@doc """
777-
A convenience function for SQL-based repositories that executes the given query.
778-
779-
See `Ecto.Adapters.SQL.query/4` for more information.
777+
Runs a custom SQL query.
778+
779+
If the query was successful, it will return an `:ok` tuple containing
780+
a map with at least two keys:
781+
* `:num_rows` - the number of rows affected
782+
* `:rows` - the result set as a list. `nil` may be returned
783+
instead of the list if the command does not yield any row
784+
as result (but still yields the number of affected rows,
785+
like a `delete` command without returning would)
786+
787+
## Options
788+
* `:log` - When false, does not log the query
789+
* `:timeout` - Execute request timeout, accepts: `:infinity` (default: `15000`);
790+
791+
## Examples
792+
iex> MyRepo.query("SELECT $1::integer + $2", [40, 2])
793+
{:ok, %{rows: [[42]], num_rows: 1}}
780794
"""
795+
@spec query(iodata(), Ecto.Adapters.SQL.query_params(), Keyword.t()) ::
796+
{:ok, query_result()} | {:error, Exception.t()}
781797
def query(sql, params \\ [], opts \\ []) do
782798
Ecto.Adapters.SQL.query(get_dynamic_repo(), sql, params, opts)
783799
end
784800

785801
@doc """
786-
A convenience function for SQL-based repositories that executes the given query.
802+
Runs a custom SQL query.
787803
788-
See `Ecto.Adapters.SQL.query!/4` for more information.
804+
Same as `query/3` but raises on invalid queries.
789805
"""
806+
@spec query(iodata(), Ecto.Adapters.SQL.query_params(), Keyword.t()) ::
807+
{:ok, query_result()} | {:error, Exception.t()}
790808
def query!(sql, params \\ [], opts \\ []) do
791809
Ecto.Adapters.SQL.query!(get_dynamic_repo(), sql, params, opts)
792810
end
793811

794812
@doc """
795-
A convenience function for SQL-based repositories that executes the given multi-result query.
813+
Runs a custom SQL query that returns multiple results on the given repo.
814+
815+
In case of success, it must return an `:ok` tuple containing
816+
a list of maps with at least two keys:
817+
818+
* `:num_rows` - the number of rows affected
819+
820+
* `:rows` - the result set as a list. `nil` may be returned
821+
instead of the list if the command does not yield any row
822+
as result (but still yields the number of affected rows,
823+
like a `delete` command without returning would)
824+
825+
## Options
826+
827+
* `:log` - When false, does not log the query
828+
* `:timeout` - Execute request timeout, accepts: `:infinity` (default: `#{@timeout}`);
829+
830+
## Examples
796831
797-
See `Ecto.Adapters.SQL.query_many/4` for more information.
832+
iex> MyRepo.query_many("SELECT $1; SELECT $2;", [40, 2])
833+
{:ok, [%{rows: [[40]], num_rows: 1}, %{rows: [[2]], num_rows: 1}]}
798834
"""
835+
836+
@spec query_many(iodata, query_params, Keyword.t()) ::
837+
{:ok, [query_result]} | {:error, Exception.t()}
799838
def query_many(sql, params \\ [], opts \\ []) do
800839
Ecto.Adapters.SQL.query_many(get_dynamic_repo(), sql, params, opts)
801840
end
802841

803842
@doc """
804-
A convenience function for SQL-based repositories that executes the given multi-result query.
805-
806-
See `Ecto.Adapters.SQL.query_many!/4` for more information.
843+
Same as `query_many/4` but raises on invalid queries.
807844
"""
845+
@spec query_many!(iodata, query_params, Keyword.t()) ::
846+
[query_result]
808847
def query_many!(sql, params \\ [], opts \\ []) do
809848
Ecto.Adapters.SQL.query_many!(get_dynamic_repo(), sql, params, opts)
810849
end
811850

812851
@doc """
813-
A convenience function for SQL-based repositories that translates the given query to SQL.
852+
Converts the given query to SQL according to its kind and the
853+
adapter in the given repository.
854+
855+
## Examples
856+
857+
The examples below are meant for reference. Each adapter will
858+
return a different result:
859+
860+
iex> MyRepo.to_sql(:all, Post)
861+
{"SELECT p.id, p.title, p.inserted_at, p.created_at FROM posts as p", []}
862+
863+
iex> MyRepo.to_sql(:update_all, from(p in Post, update: [set: [title: ^"hello"]]))
864+
{"UPDATE posts AS p SET title = $1", ["hello"]}
814865
815-
See `Ecto.Adapters.SQL.to_sql/3` for more information.
816866
"""
867+
@spec to_sql(:all | :update_all | :delete_all, Ecto.Queryable.t()) ::
868+
{String.t(), query_params}
817869
def to_sql(operation, queryable) do
818870
Ecto.Adapters.SQL.to_sql(operation, get_dynamic_repo(), queryable)
819871
end
820872

821-
@doc """
822-
A convenience function for SQL-based repositories that executes an EXPLAIN statement or similar
823-
depending on the adapter to obtain statistics for the given query.
873+
case unquote(driver) do
874+
:postgrex ->
875+
@doc """
876+
Executes an EXPLAIN statement or similar for the given query according to its kind and the
877+
adapter in the given repository.
824878
825-
See `Ecto.Adapters.SQL.explain/4` for more information.
826-
"""
879+
## Examples
880+
881+
iex> MyRepo.explain(:all, Post)
882+
"Seq Scan on posts p0 (cost=0.00..12.12 rows=1 width=443)"
883+
884+
# Shared opts
885+
iex> MyRepo.explain(:all, Post, analyze: true, timeout: 20_000)
886+
"Seq Scan on posts p0 (cost=0.00..11.70 rows=170 width=443) (actual time=0.013..0.013 rows=0 loops=1)\\nPlanning Time: 0.031 ms\\nExecution Time: 0.021 ms"
887+
888+
It's safe to execute it for updates and deletes, no data change will be committed:
889+
890+
iex> MyRepo.explain(:update_all, from(p in Post, update: [set: [title: "new title"]]))
891+
"Update on posts p0 (cost=0.00..11.70 rows=170 width=449)\\n -> Seq Scan on posts p0 (cost=0.00..11.70 rows=170 width=449)"
892+
893+
### Options
894+
895+
The built-in Postgrex adapter supports passing `opts` to the EXPLAIN statement according to the following:
896+
`:analyze`, `:verbose`, `:costs`, `:settings`, `:buffers`, `:timing`, `:summary`, `:format`, `:plan`
897+
898+
All options except `format` are boolean valued and default to `false`.
899+
900+
The allowed `format` values are `:map`, `:yaml`, and `:text`:
901+
* `:map` is the deserialized JSON encoding.
902+
* `:yaml` and `:text` return the result as a string.
903+
904+
The Postgrex adapter supports the following formats: `:map`, `:yaml` and `:text`
905+
906+
The `:plan` option in Postgrex can take the values `:custom` or `:fallback_generic`. When `:custom`
907+
is specified, the explain plan generated will consider the specific values of the query parameters
908+
that are supplied. When using `:fallback_generic`, the specific values of the query parameters will
909+
be ignored. `:fallback_generic` does not use PostgreSQL's built-in support for a generic explain
910+
plan (available as of PostgreSQL 16), but instead uses a special implementation that works for PostgreSQL
911+
versions 12 and above. Defaults to `:custom`.
912+
913+
Any other value passed to `opts` will be forwarded to the underlying adapter query function, including
914+
shared Repo options such as `:timeout`. Non built-in adapters may have specific behaviour and you should
915+
consult their documentation for more details.
916+
917+
For version compatibility, please check your database's documentation:
918+
919+
* _Postgrex_: [PostgreSQL doc](https://www.postgresql.org/docs/current/sql-explain.html).
920+
921+
"""
922+
923+
:myxql ->
924+
@doc """
925+
Executes an EXPLAIN statement or similar for the given query according to its kind and the
926+
adapter in the given repository.
927+
928+
## Examples
929+
930+
# MySQL
931+
iex> MyRepo.explain(:all, from(p in Post, where: p.title == "title")) |> IO.puts()
932+
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
933+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
934+
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
935+
| 1 | SIMPLE | p0 | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.0 | Using where |
936+
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
937+
938+
# Shared opts
939+
iex> MyRepo.explain(:all, Post, analyze: true, timeout: 20_000)
940+
"Seq Scan on posts p0 (cost=0.00..11.70 rows=170 width=443) (actual time=0.013..0.013 rows=0 loops=1)\\nPlanning Time: 0.031 ms\\nExecution Time: 0.021 ms"
941+
942+
It's safe to execute it for updates and deletes, no data change will be committed:
943+
944+
iex> MyRepo(:update_all, from(p in Post, update: [set: [title: "new title"]]))
945+
"Update on posts p0 (cost=0.00..11.70 rows=170 width=449)\\n -> Seq Scan on posts p0 (cost=0.00..11.70 rows=170 width=449)"
946+
947+
### Options
948+
949+
The MyXQL adapter supports passing `opts` to the EXPLAIN statement according to the following:
950+
951+
* `:format`
952+
953+
All options except `format` are boolean valued and default to `false`.
954+
955+
The allowed `format` values are `:map`, `:yaml`, and `:text`:
956+
* `:map` is the deserialized JSON encoding.
957+
* `:yaml` and `:text` return the result as a string.
958+
959+
The built-in adapters support the following formats: `:map` and `:text`
960+
961+
Any other value passed to `opts` will be forwarded to the underlying adapter query function, including
962+
shared Repo options such as `:timeout`. Non built-in adapters may have specific behaviour and you should
963+
consult their documentation for more details.
964+
965+
For version compatibility, please check your database's documentation:
966+
967+
* _MyXQL_: [MySQL doc](https://dev.mysql.com/doc/refman/8.0/en/explain.html).
968+
969+
"""
970+
971+
_ -> :ok
972+
end
973+
974+
@spec explain(:all | :update_all | :delete_all, Ecto.Queryable.t(), opts :: Keyword.t()) ::
975+
String.t() | Exception.t() | list(map)
827976
def explain(operation, queryable, opts \\ []) do
828977
Ecto.Adapters.SQL.explain(get_dynamic_repo(), operation, queryable, opts)
829978
end
830979

831980
@doc """
832-
A convenience function for SQL-based repositories that forces all connections in the
833-
pool to disconnect within the given interval.
834-
835-
See `Ecto.Adapters.SQL.disconnect_all/3` for more information.
981+
Forces all connections in the repo pool to disconnect within the given interval.
982+
983+
Once this function is called, the pool will disconnect all of its connections
984+
as they are checked in or as they are pinged. Checked in connections will be
985+
randomly disconnected within the given time interval. Pinged connections are
986+
immediately disconnected - as they are idle (according to `:idle_interval`).
987+
988+
If the connection has a backoff configured (which is the case by default),
989+
disconnecting means an attempt at a new connection will be done immediately
990+
after, without starting a new process for each connection. However, if backoff
991+
has been disabled, the connection process will terminate. In such cases,
992+
disconnecting all connections may cause the pool supervisor to restart
993+
depending on the max_restarts/max_seconds configuration of the pool,
994+
so you will want to set those carefully.
836995
"""
996+
@spec disconnect_all(non_neg_integer, opts :: Keyword.t()) :: :ok
837997
def disconnect_all(interval, opts \\ []) do
838998
Ecto.Adapters.SQL.disconnect_all(get_dynamic_repo(), interval, opts)
839999
end

0 commit comments

Comments
 (0)