changed CHANGELOG.md
 
@@ -1,5 +1,14 @@
1
1
# Changelog
2
2
3
+ ## v0.19.3 (2024-11-12)
4
+
5
+ * Enhancements
6
+ * Default params to in query APIs to `[]`
7
+ * Allow `:comment` as options to query APIs
8
+
9
+ * Bug fixes
10
+ * Call disconnect on protocol when reconnecting in `Postgrex.SimpleConnection`
11
+
3
12
## v0.19.2 (2024-10-23)
4
13
5
14
* Bug fixes
changed hex_metadata.config
 
@@ -1,8 +1,31 @@
1
1
{<<"links">>,[{<<"GitHub">>,<<"https://github.com/elixir-ecto/postgrex">>}]}.
2
2
{<<"name">>,<<"postgrex">>}.
3
- {<<"version">>,<<"0.19.2">>}.
3
+ {<<"version">>,<<"0.19.3">>}.
4
4
{<<"description">>,<<"PostgreSQL driver for Elixir">>}.
5
5
{<<"elixir">>,<<"~> 1.11">>}.
6
+ {<<"app">>,<<"postgrex">>}.
7
+ {<<"licenses">>,[<<"Apache-2.0">>]}.
8
+ {<<"requirements">>,
9
+ [[{<<"name">>,<<"jason">>},
10
+ {<<"app">>,<<"jason">>},
11
+ {<<"optional">>,true},
12
+ {<<"requirement">>,<<"~> 1.0">>},
13
+ {<<"repository">>,<<"hexpm">>}],
14
+ [{<<"name">>,<<"table">>},
15
+ {<<"app">>,<<"table">>},
16
+ {<<"optional">>,true},
17
+ {<<"requirement">>,<<"~> 0.1.0">>},
18
+ {<<"repository">>,<<"hexpm">>}],
19
+ [{<<"name">>,<<"decimal">>},
20
+ {<<"app">>,<<"decimal">>},
21
+ {<<"optional">>,false},
22
+ {<<"requirement">>,<<"~> 1.5 or ~> 2.0">>},
23
+ {<<"repository">>,<<"hexpm">>}],
24
+ [{<<"name">>,<<"db_connection">>},
25
+ {<<"app">>,<<"db_connection">>},
26
+ {<<"optional">>,false},
27
+ {<<"requirement">>,<<"~> 2.1">>},
28
+ {<<"repository">>,<<"hexpm">>}]]}.
6
29
{<<"files">>,
7
30
[<<"lib">>,<<"lib/postgrex">>,<<"lib/postgrex/builtins.ex">>,
8
31
<<"lib/postgrex/replication_connection.ex">>,<<"lib/postgrex/protocol.ex">>,
 
@@ -54,27 +77,4 @@
54
77
<<"lib/postgrex/parameters.ex">>,<<"lib/postgrex/app.ex">>,
55
78
<<"lib/postgrex.ex">>,<<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,
56
79
<<"CHANGELOG.md">>]}.
57
- {<<"app">>,<<"postgrex">>}.
58
- {<<"licenses">>,[<<"Apache-2.0">>]}.
59
- {<<"requirements">>,
60
- [[{<<"name">>,<<"jason">>},
61
- {<<"app">>,<<"jason">>},
62
- {<<"optional">>,true},
63
- {<<"requirement">>,<<"~> 1.0">>},
64
- {<<"repository">>,<<"hexpm">>}],
65
- [{<<"name">>,<<"table">>},
66
- {<<"app">>,<<"table">>},
67
- {<<"optional">>,true},
68
- {<<"requirement">>,<<"~> 0.1.0">>},
69
- {<<"repository">>,<<"hexpm">>}],
70
- [{<<"name">>,<<"decimal">>},
71
- {<<"app">>,<<"decimal">>},
72
- {<<"optional">>,false},
73
- {<<"requirement">>,<<"~> 1.5 or ~> 2.0">>},
74
- {<<"repository">>,<<"hexpm">>}],
75
- [{<<"name">>,<<"db_connection">>},
76
- {<<"app">>,<<"db_connection">>},
77
- {<<"optional">>,false},
78
- {<<"requirement">>,<<"~> 2.1">>},
79
- {<<"repository">>,<<"hexpm">>}]]}.
80
80
{<<"build_tools">>,[<<"mix">>]}.
changed lib/postgrex.ex
 
@@ -67,6 +67,10 @@ defmodule Postgrex do
67
67
@max_rows 500
68
68
@timeout 15_000
69
69
70
+ @comment_validation_error Postgrex.Error.exception(
71
+ message: "`:comment` option cannot contain sequence \"*/\""
72
+ )
73
+
70
74
### PUBLIC API ###
71
75
72
76
@doc """
 
@@ -169,6 +173,11 @@ defmodule Postgrex do
169
173
This is useful when using Postgrex against systems that do not support composite types
170
174
(default: `false`).
171
175
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
+
172
181
`Postgrex` uses the `DBConnection` library and supports all `DBConnection`
173
182
options like `:idle`, `:after_connect` etc. See `DBConnection.start_link/2`
174
183
for more information.
 
@@ -288,8 +297,10 @@ defmodule Postgrex do
288
297
"""
289
298
@spec query(conn, iodata, list, [execute_option]) ::
290
299
{:ok, Postgrex.Result.t()} | {:error, Exception.t()}
291
- def query(conn, statement, params, opts \\ []) do
292
- if name = Keyword.get(opts, :cache_statement) do
300
+ def query(conn, statement, params \\ [], opts \\ []) when is_list(params) and is_list(opts) do
301
+ name = Keyword.get(opts, :cache_statement)
302
+
303
+ if comment_not_present!(opts) && name do
293
304
query = %Query{name: name, cache: :statement, statement: IO.iodata_to_binary(statement)}
294
305
295
306
case DBConnection.prepare_execute(conn, query, params, opts) do
 
@@ -319,12 +330,26 @@ defmodule Postgrex do
319
330
end
320
331
end
321
332
333
+ defp comment_not_present!(opts) do
334
+ case Keyword.get(opts, :comment) do
335
+ nil ->
336
+ true
337
+
338
+ comment when is_binary(comment) ->
339
+ if String.contains?(comment, "*/") do
340
+ raise @comment_validation_error
341
+ else
342
+ false
343
+ end
344
+ end
345
+ end
346
+
322
347
@doc """
323
348
Runs an (extended) query and returns the result or raises `Postgrex.Error` if
324
349
there was an error. See `query/3`.
325
350
"""
326
351
@spec query!(conn, iodata, list, [execute_option]) :: Postgrex.Result.t()
327
- def query!(conn, statement, params, opts \\ []) do
352
+ def query!(conn, statement, params \\ [], opts \\ []) when is_list(params) and is_list(opts) do
328
353
case query(conn, statement, params, opts) do
329
354
{:ok, result} -> result
330
355
{:error, err} -> raise err
 
@@ -363,7 +388,7 @@ defmodule Postgrex do
363
388
{:ok, Postgrex.Query.t()} | {:error, Exception.t()}
364
389
def prepare(conn, name, statement, opts \\ []) do
365
390
query = %Query{name: name, statement: statement}
366
- opts = Keyword.put(opts, :postgrex_prepare, true)
391
+ opts = Keyword.put(opts, :postgrex_prepare, comment_not_present!(opts))
367
392
DBConnection.prepare(conn, query, opts)
368
393
end
369
394
 
@@ -373,7 +398,7 @@ defmodule Postgrex do
373
398
"""
374
399
@spec prepare!(conn, iodata, iodata, [option]) :: Postgrex.Query.t()
375
400
def prepare!(conn, name, statement, opts \\ []) do
376
- opts = Keyword.put(opts, :postgrex_prepare, true)
401
+ opts = Keyword.put(opts, :postgrex_prepare, comment_not_present!(opts))
377
402
DBConnection.prepare!(conn, %Query{name: name, statement: statement}, opts)
378
403
end
379
404
 
@@ -409,8 +434,9 @@ defmodule Postgrex do
409
434
"""
410
435
@spec prepare_execute(conn, iodata, iodata, list, [execute_option]) ::
411
436
{:ok, Postgrex.Query.t(), Postgrex.Result.t()} | {:error, Postgrex.Error.t()}
412
- def prepare_execute(conn, name, statement, params, opts \\ []) do
437
+ def prepare_execute(conn, name, statement, params, opts \\ []) when is_list(params) do
413
438
query = %Query{name: name, statement: statement}
439
+ opts = Keyword.put(opts, :postgrex_prepare, comment_not_present!(opts))
414
440
DBConnection.prepare_execute(conn, query, params, opts)
415
441
end
416
442
 
@@ -420,8 +446,9 @@ defmodule Postgrex do
420
446
"""
421
447
@spec prepare_execute!(conn, iodata, iodata, list, [execute_option]) ::
422
448
{Postgrex.Query.t(), Postgrex.Result.t()}
423
- def prepare_execute!(conn, name, statement, params, opts \\ []) do
449
+ def prepare_execute!(conn, name, statement, params, opts \\ []) when is_list(params) do
424
450
query = %Query{name: name, statement: statement}
451
+ opts = Keyword.put(opts, :postgrex_prepare, comment_not_present!(opts))
425
452
DBConnection.prepare_execute!(conn, query, params, opts)
426
453
end
427
454
 
@@ -455,7 +482,7 @@ defmodule Postgrex do
455
482
"""
456
483
@spec execute(conn, Postgrex.Query.t(), list, [execute_option]) ::
457
484
{:ok, Postgrex.Query.t(), Postgrex.Result.t()} | {:error, Postgrex.Error.t()}
458
- def execute(conn, query, params, opts \\ []) do
485
+ def execute(conn, query, params, opts \\ []) when is_list(params) do
459
486
DBConnection.execute(conn, query, params, opts)
460
487
end
461
488
 
@@ -465,7 +492,7 @@ defmodule Postgrex do
465
492
"""
466
493
@spec execute!(conn, Postgrex.Query.t(), list, [execute_option]) ::
467
494
Postgrex.Result.t()
468
- def execute!(conn, query, params, opts \\ []) do
495
+ def execute!(conn, query, params, opts \\ []) when is_list(params) do
469
496
DBConnection.execute!(conn, query, params, opts)
470
497
end
changed lib/postgrex/protocol.ex
 
@@ -347,8 +347,12 @@ defmodule Postgrex.Protocol do
347
347
status = new_status(opts, prepare: prepare)
348
348
349
349
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)
352
356
end
353
357
end
354
358
 
@@ -363,11 +367,12 @@ defmodule Postgrex.Protocol do
363
367
else
364
368
prepare = Keyword.get(opts, :postgrex_prepare, false)
365
369
status = new_status(opts, prepare: prepare)
370
+ comment = Keyword.get(opts, :comment)
366
371
367
372
result =
368
373
case prepare do
369
374
true -> close_parse_describe(s, status, query)
370
- false -> close_parse_describe_flush(s, status, query)
375
+ false -> close_parse_describe_flush(s, status, query, comment)
371
376
end
372
377
373
378
with {:ok, query, s} <- result do
 
@@ -1204,9 +1209,6 @@ defmodule Postgrex.Protocol do
1204
1209
1205
1210
{:disconnect, err, s} ->
1206
1211
{:disconnect, err, s}
1207
-
1208
- {:error, %Postgrex.Error{} = err, s, buffer} ->
1209
- error_ready(s, status, err, buffer)
1210
1212
end
1211
1213
end
1212
1214
 
@@ -1314,10 +1316,6 @@ defmodule Postgrex.Protocol do
1314
1316
1315
1317
{:disconnect, err, s} ->
1316
1318
{:disconnect, err, s}
1317
-
1318
- {:error, %Postgrex.Error{} = err, s, buffer} ->
1319
- status = new_status([], mode: :transaction)
1320
- error_ready(s, status, err, buffer)
1321
1319
end
1322
1320
end
1323
1321
 
@@ -1418,9 +1416,9 @@ defmodule Postgrex.Protocol do
1418
1416
parse_describe(s, status, query)
1419
1417
end
1420
1418
1421
- defp parse_describe_flush(s, %{mode: :transaction} = status, query) do
1419
+ defp parse_describe_flush(s, %{mode: :transaction} = status, query, comment) do
1422
1420
%{buffer: buffer} = s
1423
- msgs = parse_describe_msgs(query, [msg_flush()])
1421
+ msgs = parse_describe_comment_msgs(query, comment, [msg_flush()])
1424
1422
1425
1423
with :ok <- msg_send(%{s | buffer: nil}, msgs, buffer),
1426
1424
{:ok, %Query{ref: ref} = query, %{postgres: postgres} = s, buffer} <-
 
@@ -1442,11 +1440,12 @@ defmodule Postgrex.Protocol do
1442
1440
defp parse_describe_flush(
1443
1441
%{postgres: :transaction, buffer: buffer} = s,
1444
1442
%{mode: :savepoint} = status,
1445
- query
1443
+ query,
1444
+ comment
1446
1445
) do
1447
1446
msgs =
1448
1447
[msg_query(statement: "SAVEPOINT postgrex_query")] ++
1449
- parse_describe_msgs(query, [msg_flush()])
1448
+ parse_describe_comment_msgs(query, comment, [msg_flush()])
1450
1449
1451
1450
with :ok <- msg_send(%{s | buffer: nil}, msgs, buffer),
1452
1451
{:ok, _, %{buffer: buffer} = s} <- recv_transaction(s, status, buffer),
 
@@ -1466,7 +1465,7 @@ defmodule Postgrex.Protocol do
1466
1465
end
1467
1466
end
1468
1467
1469
- defp parse_describe_flush(%{postgres: postgres} = s, %{mode: :savepoint}, _)
1468
+ defp parse_describe_flush(%{postgres: postgres} = s, %{mode: :savepoint}, _, _)
1470
1469
when postgres in [:idle, :error] do
1471
1470
transaction_error(s, postgres)
1472
1471
end
 
@@ -1530,11 +1529,13 @@ defmodule Postgrex.Protocol do
1530
1529
transaction_error(s, postgres)
1531
1530
end
1532
1531
1533
- defp close_parse_describe_flush(s, %{mode: :transaction} = status, query) do
1532
+ defp close_parse_describe_flush(s, %{mode: :transaction} = status, query, comment) do
1534
1533
%Query{name: name} = query
1535
1534
%{buffer: buffer} = s
1536
1535
1537
- msgs = [msg_close(type: :statement, name: name)] ++ parse_describe_msgs(query, [msg_flush()])
1536
+ msgs =
1537
+ [msg_close(type: :statement, name: name)] ++
1538
+ parse_describe_comment_msgs(query, comment, [msg_flush()])
1538
1539
1539
1540
with :ok <- msg_send(%{s | buffer: nil}, msgs, buffer),
1540
1541
{:ok, s, buffer} <- recv_close(s, status, buffer),
 
@@ -1558,7 +1559,8 @@ defmodule Postgrex.Protocol do
1558
1559
defp close_parse_describe_flush(
1559
1560
%{postgres: :transaction, buffer: buffer} = s,
1560
1561
%{mode: :savepoint} = status,
1561
- query
1562
+ query,
1563
+ comment
1562
1564
) do
1563
1565
%Query{name: name} = query
1564
1566
 
@@ -1566,7 +1568,7 @@ defmodule Postgrex.Protocol do
1566
1568
[
1567
1569
msg_query(statement: "SAVEPOINT postgrex_query"),
1568
1570
msg_close(type: :statement, name: name)
1569
- ] ++ parse_describe_msgs(query, [msg_flush()])
1571
+ ] ++ parse_describe_comment_msgs(query, comment, [msg_flush()])
1570
1572
1571
1573
with :ok <- msg_send(%{s | buffer: nil}, msgs, buffer),
1572
1574
{:ok, _, %{buffer: buffer} = s} <- recv_transaction(s, status, buffer),
 
@@ -1588,11 +1590,21 @@ defmodule Postgrex.Protocol do
1588
1590
end
1589
1591
end
1590
1592
1591
- defp close_parse_describe_flush(%{postgres: postgres} = s, %{mode: :savepoint}, _)
1593
+ defp close_parse_describe_flush(%{postgres: postgres} = s, %{mode: :savepoint}, _, _)
1592
1594
when postgres in [:idle, :error] do
1593
1595
transaction_error(s, postgres)
1594
1596
end
1595
1597
1598
+ defp parse_describe_comment_msgs(query, comment, tail) when is_binary(comment) do
1599
+ statement = [query.statement, "/*", comment, "*/"]
1600
+ query = %{query | statement: statement}
1601
+ parse_describe_msgs(query, tail)
1602
+ end
1603
+
1604
+ defp parse_describe_comment_msgs(query, _comment, tail) do
1605
+ parse_describe_msgs(query, tail)
1606
+ end
1607
+
1596
1608
defp parse_describe_msgs(query, tail) do
1597
1609
%Query{name: name, statement: statement, param_oids: param_oids} = query
1598
1610
type_oids = param_oids || []
 
@@ -2079,7 +2091,7 @@ defmodule Postgrex.Protocol do
2079
2091
2080
2092
_ ->
2081
2093
# flush awaiting execute or declare
2082
- parse_describe_flush(s, status, query)
2094
+ parse_describe_flush(s, status, query, nil)
2083
2095
end
2084
2096
end
2085
2097
 
@@ -2105,7 +2117,7 @@ defmodule Postgrex.Protocol do
2105
2117
defp handle_prepare_execute(%Query{name: ""} = query, params, opts, s) do
2106
2118
status = new_status(opts)
2107
2119
2108
- case parse_describe_flush(s, status, query) do
2120
+ case parse_describe_flush(s, status, query, nil) do
2109
2121
{:ok, query, s} ->
2110
2122
bind_execute_close(s, status, query, params)
2111
2123
 
@@ -2117,7 +2129,7 @@ defmodule Postgrex.Protocol do
2117
2129
defp handle_prepare_execute(%Query{} = query, params, opts, s) do
2118
2130
status = new_status(opts)
2119
2131
2120
- case close_parse_describe_flush(s, status, query) do
2132
+ case close_parse_describe_flush(s, status, query, nil) do
2121
2133
{:ok, query, s} ->
2122
2134
bind_execute(s, status, query, params)
2123
2135
 
@@ -2396,7 +2408,7 @@ defmodule Postgrex.Protocol do
2396
2408
defp handle_prepare_bind(%Query{name: ""} = query, params, res, opts, s) do
2397
2409
status = new_status(opts)
2398
2410
2399
- case parse_describe_flush(s, status, query) do
2411
+ case parse_describe_flush(s, status, query, nil) do
2400
2412
{:ok, query, s} ->
2401
2413
bind(s, status, query, params, res)
2402
2414
 
@@ -2408,7 +2420,7 @@ defmodule Postgrex.Protocol do
2408
2420
defp handle_prepare_bind(%Query{} = query, params, res, opts, s) do
2409
2421
status = new_status(opts)
2410
2422
2411
- case close_parse_describe_flush(s, status, query) do
2423
+ case close_parse_describe_flush(s, status, query, nil) do
2412
2424
{:ok, query, s} ->
2413
2425
bind(s, status, query, params, res)
2414
2426
 
@@ -2978,14 +2990,6 @@ defmodule Postgrex.Protocol do
2978
2990
2979
2991
{:disconnect, err, s} ->
2980
2992
{:disconnect, err, s}
2981
-
2982
- {:error, %Postgrex.Error{} = err, s, buffer} ->
2983
- # We convert {:error, err, state} to {:error, state}
2984
- # so that DBConnection will disconnect during handle_begin/handle_rollback
2985
- # and will attempt to rollback during handle_commit
2986
- with {:error, _err, s} <- error_ready(s, status, err, buffer) do
2987
- {:error, s}
2988
- end
2989
2993
end
2990
2994
end
2991
2995
 
@@ -3267,7 +3271,7 @@ defmodule Postgrex.Protocol do
3267
3271
disconnect(s, :tcp, "async_recv", reason, :active_once)
3268
3272
after
3269
3273
timeout ->
3270
- disconnect(s, :tcp, "async_recv", :timeout, :active_one)
3274
+ disconnect(s, :tcp, "async_recv", :timeout, :active_once)
3271
3275
end
3272
3276
end
changed lib/postgrex/simple_connection.ex
 
@@ -337,7 +337,12 @@ defmodule Postgrex.SimpleConnection do
337
337
@impl :gen_statem
338
338
def handle_event(type, content, statem_state, state)
339
339
340
- def handle_event(:internal, {:connect, _}, @state, %{state: {mod, mod_state}} = state) do
340
+ def handle_event(:internal, {:connect, :reconnect}, @state, %{protocol: protocol} = state) do
341
+ Protocol.disconnect(:reconnect, protocol)
342
+ {:keep_state, %{state | protocol: nil}, {:next_event, :internal, {:connect, :init}}}
343
+ end
344
+
345
+ def handle_event(:internal, {:connect, :init}, @state, %{state: {mod, mod_state}} = state) do
341
346
opts =
342
347
case Keyword.get(opts(mod), :configure) do
343
348
{module, fun, args} -> apply(module, fun, [opts(mod) | args])
changed mix.exs
 
@@ -2,7 +2,7 @@ defmodule Postgrex.Mixfile do
2
2
use Mix.Project
3
3
4
4
@source_url "https://github.com/elixir-ecto/postgrex"
5
- @version "0.19.2"
5
+ @version "0.19.3"
6
6
7
7
def project do
8
8
[