@@ -771,69 +771,229 @@ defmodule Ecto.Adapters.SQL do
771771 ## Callbacks
772772
773773 @ doc false
774- def __before_compile__ ( _driver , _env ) do
775- quote do
776- @ doc """
777- A convenience function for SQL-based repositories that executes the given query.
774+ def __before_compile__ ( driver , _env ) do
775+ default_timeout = @ timeout
778776
779- See `Ecto.Adapters.SQL.query/4` for more information.
777+ quote bind_quoted: [ driver: driver , default_timeout: default_timeout ] do
778+ @ doc """
779+ Runs a custom SQL query.
780+
781+ If the query was successful, it will return an `:ok` tuple containing
782+ a map with at least two keys:
783+ * `:num_rows` - the number of rows affected
784+ * `:rows` - the result set as a list. `nil` may be returned
785+ instead of the list if the command does not yield any row
786+ as result (but still yields the number of affected rows,
787+ like a `delete` command without returning would)
788+
789+ ## Options
790+ * `:log` - When false, does not log the query
791+ * `:timeout` - Execute request timeout, accepts: `:infinity` (default: `#{ default_timeout } `);
792+
793+ ## Examples
794+ iex> MyRepo.query("SELECT $1::integer + $2", [40, 2])
795+ {:ok, %{rows: [[42]], num_rows: 1}}
780796 """
797+ @ spec query ( iodata ( ) , Ecto.Adapters.SQL . query_params ( ) , Keyword . t ( ) ) ::
798+ { :ok , Ecto.Adapters.SQL . query_result ( ) } | { :error , Exception . t ( ) }
781799 def query ( sql , params \\ [ ] , opts \\ [ ] ) do
782800 Ecto.Adapters.SQL . query ( get_dynamic_repo ( ) , sql , params , opts )
783801 end
784802
785803 @ doc """
786- A convenience function for SQL-based repositories that executes the given query.
804+ Runs a custom SQL query.
787805
788- See `Ecto.Adapters.SQL. query!/4` for more information .
806+ Same as ` query/3` but raises on invalid queries .
789807 """
808+ @ spec query ( iodata ( ) , Ecto.Adapters.SQL . query_params ( ) , Keyword . t ( ) ) ::
809+ { :ok , Ecto.Adapters.SQL . query_result ( ) } | { :error , Exception . t ( ) }
790810 def query! ( sql , params \\ [ ] , opts \\ [ ] ) do
791811 Ecto.Adapters.SQL . query! ( get_dynamic_repo ( ) , sql , params , opts )
792812 end
793813
794814 @ doc """
795- A convenience function for SQL-based repositories that executes the given multi-result query.
815+ Runs a custom SQL query that returns multiple results on the given repo.
816+
817+ In case of success, it must return an `:ok` tuple containing
818+ a list of maps with at least two keys:
819+
820+ * `:num_rows` - the number of rows affected
821+
822+ * `:rows` - the result set as a list. `nil` may be returned
823+ instead of the list if the command does not yield any row
824+ as result (but still yields the number of affected rows,
825+ like a `delete` command without returning would)
826+
827+ ## Options
796828
797- See `Ecto.Adapters.SQL.query_many/4` for more information.
829+ * `:log` - When false, does not log the query
830+ * `:timeout` - Execute request timeout, accepts: `:infinity` (default: `#{ default_timeout } `);
831+
832+ ## Examples
833+
834+ iex> MyRepo.query_many("SELECT $1; SELECT $2;", [40, 2])
835+ {:ok, [%{rows: [[40]], num_rows: 1}, %{rows: [[2]], num_rows: 1}]}
798836 """
837+
838+ @ spec query_many ( iodata , Ecto.Adapters.SQL . query_params ( ) , Keyword . t ( ) ) ::
839+ { :ok , [ Ecto.Adapters.SQL . query_result ] } | { :error , Exception . t ( ) }
799840 def query_many ( sql , params \\ [ ] , opts \\ [ ] ) do
800841 Ecto.Adapters.SQL . query_many ( get_dynamic_repo ( ) , sql , params , opts )
801842 end
802843
803844 @ 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.
845+ Same as `query_many/4` but raises on invalid queries.
807846 """
847+ @ spec query_many! ( iodata , Ecto.Adapters.SQL . query_params ( ) , Keyword . t ( ) ) ::
848+ [ Ecto.Adapters.SQL . query_result ]
808849 def query_many! ( sql , params \\ [ ] , opts \\ [ ] ) do
809850 Ecto.Adapters.SQL . query_many! ( get_dynamic_repo ( ) , sql , params , opts )
810851 end
811852
812853 @ doc """
813- A convenience function for SQL-based repositories that translates the given query to SQL.
854+ Converts the given query to SQL according to its kind and the
855+ adapter in the given repository.
856+
857+ ## Examples
858+
859+ The examples below are meant for reference. Each adapter will
860+ return a different result:
861+
862+ iex> MyRepo.to_sql(:all, Post)
863+ {"SELECT p.id, p.title, p.inserted_at, p.created_at FROM posts as p", []}
864+
865+ iex> MyRepo.to_sql(:update_all, from(p in Post, update: [set: [title: ^"hello"]]))
866+ {"UPDATE posts AS p SET title = $1", ["hello"]}
814867
815- See `Ecto.Adapters.SQL.to_sql/3` for more information.
816868 """
869+ @ spec to_sql ( :all | :update_all | :delete_all , Ecto.Queryable . t ( ) ) ::
870+ { String . t ( ) , Ecto.Adapters.SQL . query_params ( ) }
817871 def to_sql ( operation , queryable ) do
818872 Ecto.Adapters.SQL . to_sql ( operation , get_dynamic_repo ( ) , queryable )
819873 end
820874
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.
875+ case driver do
876+ :postgrex ->
877+ @ doc """
878+ Executes an EXPLAIN statement or similar for the given query according to its kind and the
879+ adapter in the given repository.
824880
825- See `Ecto.Adapters.SQL.explain/4` for more information.
826- """
881+ ## Examples
882+
883+ iex> MyRepo.explain(:all, Post)
884+ "Seq Scan on posts p0 (cost=0.00..12.12 rows=1 width=443)"
885+
886+ # Shared opts
887+ iex> MyRepo.explain(:all, Post, analyze: true, timeout: 20_000)
888+ "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"
889+
890+ It's safe to execute it for updates and deletes, no data change will be committed:
891+
892+ iex> MyRepo.explain(:update_all, from(p in Post, update: [set: [title: "new title"]]))
893+ "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)"
894+
895+ ### Options
896+
897+ The built-in Postgrex adapter supports passing `opts` to the EXPLAIN statement according to the following:
898+ `:analyze`, `:verbose`, `:costs`, `:settings`, `:buffers`, `:timing`, `:summary`, `:format`, `:plan`
899+
900+ All options except `format` are boolean valued and default to `false`.
901+
902+ The allowed `format` values are `:map`, `:yaml`, and `:text`:
903+ * `:map` is the deserialized JSON encoding.
904+ * `:yaml` and `:text` return the result as a string.
905+
906+ The Postgrex adapter supports the following formats: `:map`, `:yaml` and `:text`
907+
908+ The `:plan` option in Postgrex can take the values `:custom` or `:fallback_generic`. When `:custom`
909+ is specified, the explain plan generated will consider the specific values of the query parameters
910+ that are supplied. When using `:fallback_generic`, the specific values of the query parameters will
911+ be ignored. `:fallback_generic` does not use PostgreSQL's built-in support for a generic explain
912+ plan (available as of PostgreSQL 16), but instead uses a special implementation that works for PostgreSQL
913+ versions 12 and above. Defaults to `:custom`.
914+
915+ Any other value passed to `opts` will be forwarded to the underlying adapter query function, including
916+ shared Repo options such as `:timeout`. Non built-in adapters may have specific behaviour and you should
917+ consult their documentation for more details.
918+
919+ For version compatibility, please check your database's documentation:
920+
921+ * _Postgrex_: [PostgreSQL doc](https://www.postgresql.org/docs/current/sql-explain.html).
922+
923+ """
924+
925+ :myxql ->
926+ @ doc """
927+ Executes an EXPLAIN statement or similar for the given query according to its kind and the
928+ adapter in the given repository.
929+
930+ ## Examples
931+
932+ # MySQL
933+ iex> MyRepo.explain(:all, from(p in Post, where: p.title == "title")) |> IO.puts()
934+ +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
935+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
936+ +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
937+ | 1 | SIMPLE | p0 | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.0 | Using where |
938+ +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
939+
940+ # Shared opts
941+ iex> MyRepo.explain(:all, Post, analyze: true, timeout: 20_000)
942+ "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"
943+
944+ It's safe to execute it for updates and deletes, no data change will be committed:
945+
946+ iex> MyRepo(:update_all, from(p in Post, update: [set: [title: "new title"]]))
947+ "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)"
948+
949+ ### Options
950+
951+ The MyXQL adapter supports passing `opts` to the EXPLAIN statement according to the following:
952+
953+ * `:format`
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+ _ ->
972+ :ok
973+ end
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