Skip to content

Commit 4971a27

Browse files
authored
Allow to append comment to query (#709)
1 parent 6de403e commit 4971a27

File tree

3 files changed

+63
-12
lines changed

3 files changed

+63
-12
lines changed

lib/postgrex.ex

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ defmodule Postgrex do
6767
@max_rows 500
6868
@timeout 15_000
6969

70+
@comment_validation_error Postgrex.Error.exception(
71+
message: "`:comment` option cannot contain sequence \"*/\""
72+
)
73+
7074
### PUBLIC API ###
7175

7276
@doc """
@@ -169,6 +173,11 @@ defmodule Postgrex do
169173
This is useful when using Postgrex against systems that do not support composite types
170174
(default: `false`).
171175
176+
* `:comment` - When a binary string is provided, appends the given text as a comment to the
177+
query. This can be useful for tracing purposes, such as when using SQLCommenter or similar
178+
tools to track query performance and behavior. Note that including a comment disables query
179+
caching since each query with a different comment is treated as unique (default: `nil`).
180+
172181
`Postgrex` uses the `DBConnection` library and supports all `DBConnection`
173182
options like `:idle`, `:after_connect` etc. See `DBConnection.start_link/2`
174183
for more information.
@@ -289,6 +298,8 @@ defmodule Postgrex do
289298
@spec query(conn, iodata, list, [execute_option]) ::
290299
{:ok, Postgrex.Result.t()} | {:error, Exception.t()}
291300
def query(conn, statement, params, opts \\ []) do
301+
:ok = validate_comment(opts)
302+
292303
if name = Keyword.get(opts, :cache_statement) do
293304
query = %Query{name: name, cache: :statement, statement: IO.iodata_to_binary(statement)}
294305

@@ -319,6 +330,20 @@ defmodule Postgrex do
319330
end
320331
end
321332

333+
defp validate_comment(opts) do
334+
case Keyword.get(opts, :comment) do
335+
nil ->
336+
:ok
337+
338+
comment when is_binary(comment) ->
339+
if String.contains?(comment, "*/") do
340+
raise @comment_validation_error
341+
else
342+
:ok
343+
end
344+
end
345+
end
346+
322347
@doc """
323348
Runs an (extended) query and returns the result or raises `Postgrex.Error` if
324349
there was an error. See `query/3`.
@@ -363,7 +388,8 @@ defmodule Postgrex do
363388
{:ok, Postgrex.Query.t()} | {:error, Exception.t()}
364389
def prepare(conn, name, statement, opts \\ []) do
365390
query = %Query{name: name, statement: statement}
366-
opts = Keyword.put(opts, :postgrex_prepare, true)
391+
prepare? = Keyword.get(opts, :comment) == nil
392+
opts = Keyword.put(opts, :postgrex_prepare, prepare?)
367393
DBConnection.prepare(conn, query, opts)
368394
end
369395

@@ -373,7 +399,8 @@ defmodule Postgrex do
373399
"""
374400
@spec prepare!(conn, iodata, iodata, [option]) :: Postgrex.Query.t()
375401
def prepare!(conn, name, statement, opts \\ []) do
376-
opts = Keyword.put(opts, :postgrex_prepare, true)
402+
prepare? = Keyword.get(opts, :comment) == nil
403+
opts = Keyword.put(opts, :postgrex_prepare, prepare?)
377404
DBConnection.prepare!(conn, %Query{name: name, statement: statement}, opts)
378405
end
379406

lib/postgrex/protocol.ex

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -347,8 +347,12 @@ defmodule Postgrex.Protocol do
347347
status = new_status(opts, prepare: prepare)
348348

349349
case prepare do
350-
true -> parse_describe_close(s, status, query)
351-
false -> parse_describe_flush(s, status, query)
350+
true ->
351+
parse_describe_close(s, status, query)
352+
353+
false ->
354+
comment = Keyword.get(opts, :comment)
355+
parse_describe_flush(s, status, query, comment)
352356
end
353357
end
354358

@@ -1418,9 +1422,9 @@ defmodule Postgrex.Protocol do
14181422
parse_describe(s, status, query)
14191423
end
14201424

1421-
defp parse_describe_flush(s, %{mode: :transaction} = status, query) do
1425+
defp parse_describe_flush(s, %{mode: :transaction} = status, query, comment) do
14221426
%{buffer: buffer} = s
1423-
msgs = parse_describe_msgs(query, [msg_flush()])
1427+
msgs = parse_describe_comment_msgs(query, comment, [msg_flush()])
14241428

14251429
with :ok <- msg_send(%{s | buffer: nil}, msgs, buffer),
14261430
{:ok, %Query{ref: ref} = query, %{postgres: postgres} = s, buffer} <-
@@ -1442,11 +1446,12 @@ defmodule Postgrex.Protocol do
14421446
defp parse_describe_flush(
14431447
%{postgres: :transaction, buffer: buffer} = s,
14441448
%{mode: :savepoint} = status,
1445-
query
1449+
query,
1450+
comment
14461451
) do
14471452
msgs =
14481453
[msg_query(statement: "SAVEPOINT postgrex_query")] ++
1449-
parse_describe_msgs(query, [msg_flush()])
1454+
parse_describe_comment_msgs(query, comment, [msg_flush()])
14501455

14511456
with :ok <- msg_send(%{s | buffer: nil}, msgs, buffer),
14521457
{:ok, _, %{buffer: buffer} = s} <- recv_transaction(s, status, buffer),
@@ -1466,7 +1471,7 @@ defmodule Postgrex.Protocol do
14661471
end
14671472
end
14681473

1469-
defp parse_describe_flush(%{postgres: postgres} = s, %{mode: :savepoint}, _)
1474+
defp parse_describe_flush(%{postgres: postgres} = s, %{mode: :savepoint}, _, _)
14701475
when postgres in [:idle, :error] do
14711476
transaction_error(s, postgres)
14721477
end
@@ -1593,6 +1598,16 @@ defmodule Postgrex.Protocol do
15931598
transaction_error(s, postgres)
15941599
end
15951600

1601+
defp parse_describe_comment_msgs(query, comment, tail) when is_binary(comment) do
1602+
statement = [query.statement, "/*", comment, "*/"]
1603+
query = %{query | statement: statement}
1604+
parse_describe_msgs(query, tail)
1605+
end
1606+
1607+
defp parse_describe_comment_msgs(query, _comment, tail) do
1608+
parse_describe_msgs(query, tail)
1609+
end
1610+
15961611
defp parse_describe_msgs(query, tail) do
15971612
%Query{name: name, statement: statement, param_oids: param_oids} = query
15981613
type_oids = param_oids || []
@@ -2079,7 +2094,7 @@ defmodule Postgrex.Protocol do
20792094

20802095
_ ->
20812096
# flush awaiting execute or declare
2082-
parse_describe_flush(s, status, query)
2097+
parse_describe_flush(s, status, query, nil)
20832098
end
20842099
end
20852100

@@ -2105,7 +2120,7 @@ defmodule Postgrex.Protocol do
21052120
defp handle_prepare_execute(%Query{name: ""} = query, params, opts, s) do
21062121
status = new_status(opts)
21072122

2108-
case parse_describe_flush(s, status, query) do
2123+
case parse_describe_flush(s, status, query, nil) do
21092124
{:ok, query, s} ->
21102125
bind_execute_close(s, status, query, params)
21112126

@@ -2396,7 +2411,7 @@ defmodule Postgrex.Protocol do
23962411
defp handle_prepare_bind(%Query{name: ""} = query, params, res, opts, s) do
23972412
status = new_status(opts)
23982413

2399-
case parse_describe_flush(s, status, query) do
2414+
case parse_describe_flush(s, status, query, nil) do
24002415
{:ok, query, s} ->
24012416
bind(s, status, query, params, res)
24022417

test/query_test.exs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,6 +1851,15 @@ defmodule QueryTest do
18511851
assert [["1", "2"], ["3", "4"]] = query("COPY (VALUES (1, 2), (3, 4)) TO STDOUT", [], opts)
18521852
end
18531853

1854+
test "comment", context do
1855+
assert [[123]] = query("select 123", [], comment: "query comment goes here")
1856+
1857+
assert_raise Postgrex.Error, fn ->
1858+
query("select 123", [], comment: "*/ DROP TABLE 123 --")
1859+
end
1860+
end
1861+
1862+
@tag :big_binary
18541863
test "receive packet with remainder greater than 64MB", context do
18551864
# to ensure remainder is more than 64MB use 64MBx2+1
18561865
big_binary = :binary.copy(<<1>>, 128 * 1024 * 1024 + 1)

0 commit comments

Comments
 (0)