Thread: BUG #18544: Setting local config_parameter to DEFAULT behaves unexpectedly when using period in config name
BUG #18544: Setting local config_parameter to DEFAULT behaves unexpectedly when using period in config name
From
PG Bug reporting form
Date:
The following bug has been logged on the website: Bug reference: 18544 Logged by: Hayden Sim Email address: haydenwillsim@gmail.com PostgreSQL version: 16.3 Operating system: Docker Image (Ubuntu 24.04) Description: When a connection is initialised, calling `SELECT current_setting('hasura.user', 't');`, as expected will return a NULL. However if you call `SET "hasura.user" TO DEFAULT;`, this will actually intitialise the value to an empty string. Note this only occurs when using a period in the config name. This can be seen in the following logs: ``` psql (16.3 (Ubuntu 16.3-0ubuntu0.24.04.1)) Type "help" for help. postgres=# \pset null [NULL] Null display is "[NULL]". postgres=# SELECT current_setting('server_version_num'); current_setting ----------------- 160003 (1 row) postgres=# SELECT current_setting('foo'); ERROR: unrecognized configuration parameter "foo" postgres=# SET "foo" TO DEFAULT; ERROR: unrecognized configuration parameter "foo" postgres=# SELECT current_setting('hasura.user'); ERROR: unrecognized configuration parameter "hasura.user" postgres=# SET "hasura.user" TO DEFAULT; SET postgres=# SELECT current_setting('hasura.user'); current_setting ----------------- (1 row) ``` This presents a huge problem, since even when called inside a transaction and using `SET LOCAL`. The newly corrupted config value will affect the session even after ROLLBACK. This can be seen here: ``` psql (16.3 (Ubuntu 16.3-0ubuntu0.24.04.1)) Type "help" for help. postgres=# SELECT current_setting('server_version_num'); current_setting ----------------- 160003 (1 row) postgres=# \pset null [NULL] Null display is "[NULL]". postgres=# SELECT current_setting('hasura.user', 't'); current_setting ----------------- [NULL] (1 row) postgres=# BEGIN; BEGIN postgres=*# SELECT current_setting('hasura.user', 't'); current_setting ----------------- [NULL] (1 row) postgres=*# SET LOCAL "hasura.user" TO DEFAULT; SET postgres=*# SELECT current_setting('hasura.user', 't'); current_setting ----------------- (1 row) postgres=*# ROLLBACK; ROLLBACK postgres=# SELECT current_setting('hasura.user', 't'); current_setting ----------------- (1 row) postgres=# SELECT current_setting('hasura.user'); current_setting ----------------- (1 row) ``` Thank you! Hayden
Re: BUG #18544: Setting local config_parameter to DEFAULT behaves unexpectedly when using period in config name
From
Aleksander Alekseev
Date:
Hi, > However if you call `SET "hasura.user" TO DEFAULT;`, this will actually > intitialise the value to an empty string. The documentation for SET says [1]: """ DEFAULT can be written to specify resetting the parameter to its default value (that is, whatever value it would have had if no SET had been executed in the current session). """ And for current_setting() [2]: """ Returns the current value of the setting setting_name. If there is no such setting, current_setting throws an error unless missing_ok is supplied and is true (in which case NULL is returned). "" Personally I find it confusing. If no SET had been executed, current_setting() throws an error because there is no such setting. Reading this literally I would expect that SET ... DEFAULT should destroy the setting. It seems to me that the actual behavior is correct. Even if not, changing it would mean breaking backward compatibility. I believe an actionable item would be to better document what SET and current_setting() do in three cases: - there is no given setting - there is a setting with default value - there is a setting with non-default value Thoughts? [1]: https://www.postgresql.org/docs/current/sql-set.html [2]: https://www.postgresql.org/docs/current/functions-admin.html -- Best regards, Aleksander Alekseev
Re: BUG #18544: Setting local config_parameter to DEFAULT behaves unexpectedly when using period in config name
From
Hayden Sim
Date:
Hi,
Thank you,
I think the most confusing part is the documentation saying:
"""
DEFAULT can be written to specify resetting the parameter to its
default value (that is, whatever value it would have had if no SET had
been executed in the current session).
"""
"""
DEFAULT can be written to specify resetting the parameter to its
default value (that is, whatever value it would have had if no SET had
been executed in the current session).
"""
It would be expected that subsequent calls to `current_setting()` should throw an error (without the "t" parameter.) As would have been the case if `SET` hadn't ever been executed in the session. This is increasingly confusing when a call to `SET LOCAL "foo.bar" TO 'baz';` runs inside a transaction, as this will affect behaviour outside of the transaction. As can be seen here:
```
psql (16.3 (Ubuntu 16.3-0ubuntu0.24.04.1))
Type "help" for help.
postgres=# SELECT current_setting('foo.bar');
ERROR: unrecognized configuration parameter "foo.bar"
postgres=# BEGIN;
BEGIN
postgres=*# SET LOCAL "foo.bar" TO 'baz';
SET
postgres=*# SELECT current_setting('foo.bar');
current_setting
-----------------
baz
(1 row)
postgres=*# COMMIT;
COMMIT
postgres=# SELECT current_setting('foo.bar');
current_setting
-----------------
(1 row)
Type "help" for help.
postgres=# SELECT current_setting('foo.bar');
ERROR: unrecognized configuration parameter "foo.bar"
postgres=# BEGIN;
BEGIN
postgres=*# SET LOCAL "foo.bar" TO 'baz';
SET
postgres=*# SELECT current_setting('foo.bar');
current_setting
-----------------
baz
(1 row)
postgres=*# COMMIT;
COMMIT
postgres=# SELECT current_setting('foo.bar');
current_setting
-----------------
(1 row)
```
Hayden
On Thu, Jul 18, 2024 at 9:46 PM Aleksander Alekseev <aleksander@timescale.com> wrote:
Hi,
> However if you call `SET "hasura.user" TO DEFAULT;`, this will actually
> intitialise the value to an empty string.
The documentation for SET says [1]:
"""
DEFAULT can be written to specify resetting the parameter to its
default value (that is, whatever value it would have had if no SET had
been executed in the current session).
"""
And for current_setting() [2]:
"""
Returns the current value of the setting setting_name. If there is no
such setting, current_setting throws an error unless missing_ok is
supplied and is true (in which case NULL is returned).
""
Personally I find it confusing. If no SET had been executed,
current_setting() throws an error because there is no such setting.
Reading this literally I would expect that SET ... DEFAULT should
destroy the setting.
It seems to me that the actual behavior is correct. Even if not,
changing it would mean breaking backward compatibility.
I believe an actionable item would be to better document what SET and
current_setting() do in three cases:
- there is no given setting
- there is a setting with default value
- there is a setting with non-default value
Thoughts?
[1]: https://www.postgresql.org/docs/current/sql-set.html
[2]: https://www.postgresql.org/docs/current/functions-admin.html
--
Best regards,
Aleksander Alekseev
Re: BUG #18544: Setting local config_parameter to DEFAULT behaves unexpectedly when using period in config name
From
"David G. Johnston"
Date:
On Thursday, July 18, 2024, Hayden Sim <haydenwillsim@gmail.com> wrote:
Hi,I think the most confusing part is the documentation saying:
"""
DEFAULT can be written to specify resetting the parameter to its
default value (that is, whatever value it would have had if no SET had
been executed in the current session).
"""It would be expected that subsequent calls to `current_setting()` should throw an error (without the "t" parameter.) As would have been the case if `SET` hadn't ever been executed in the session. This is increasingly confusing when a call to `SET LOCAL "foo.bar" TO 'baz';` runs inside a transaction, as this will affect behaviour outside of the transaction. As can be seen here:
I need to review exactly what I changed but my Document NULL patch in the commitfest does cover this behavior. In short you cannot rely on either the error or NULL to represent unknown/unset. The system was not designed to allow settings that were not externally defined to the session to be used. If externally defined every setting both exists and has a non-null value.
David J.
Re: BUG #18544: Setting local config_parameter to DEFAULT behaves unexpectedly when using period in config name
From
Tom Lane
Date:
PG Bug reporting form <noreply@postgresql.org> writes: > When a connection is initialised, calling `SELECT > current_setting('hasura.user', 't');`, as expected will return a NULL. > However if you call `SET "hasura.user" TO DEFAULT;`, this will actually > intitialise the value to an empty string. That's a side effect of SET causing such a custom GUC to spring into existence --- with an empty-string default, because there is no better value. > This presents a huge problem, If you don't like it, don't use custom GUCs. They're not officially supported --- the only reason this is allowed at all is to allow setting of an extension's GUCs before the extension is loaded. See https://www.postgresql.org/docs/current/runtime-config-custom.html regards, tom lane
Re: BUG #18544: Setting local config_parameter to DEFAULT behaves unexpectedly when using period in config name
From
Hayden Sim
Date:
I understand it is a side effect of SET causing the custom GUC to be initialised. But the behaviour of `SET LOCAL` affecting the entire session, even outside of the transaction seems bizarre. Should exiting the transaction or calling `SET ... TO DEFAULT` not cause the parameter to be deleted?
```
SELECT current_setting('param.value', 't'); -- is NULL
BEGIN;
SET LOCAL "param.value" TO 'some_value';
COMMIT;
SELECT current_setting('param.value', 't'); -- is empty string
```
This is extremely uncommon, but presented a problem when using Hasura Audit Logging (relevant docs) with PGBouncer, as Hasura relied on this functionality and tried to assign this value to a JSON parameter inside of a trigger.
The repro steps are as follows:
- Hasura connects to PGB
- Hasura sends transaction which looks like
```
BEGIN;
SET LOCAL "hasura.user" = '{"x-hasura-role": "role", ... various session variables}'
-- Some mutation ...
COMMIT;
``` - Another SQL Client connects to PGB and is given the same underlying SQL connection as the previous Hasura connection.
- SQL Client triggers a Hasura trigger that expects this value to be set
```
UPDATE tableWithTrigger ...;
``` - SQL Client is faced with a JSON parse exception
Example of a trigger:
```
...
DECLARE
session_variables json;
...
Thank you,
BEGIN
...
session_variables = current_setting('hasura.user', 't');
```
In this case, NULL is an acceptable value and translates to JSON `null`, however an empty string will cause an invalid JSON exception to be raised and thus fail the trigger and the whole write.
We've lodged a bug request with Hasura and asked them to expect in their triggers that the value could potentially be an empty string.
Though I still believe this behaviour is unexpected and if `SET LOCAL` exists, it would be expected that the GUC is returned to its previous state of being uninitialised, after the transaction has been finalised.
Hayden
On Thu, Jul 18, 2024 at 11:55 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
PG Bug reporting form <noreply@postgresql.org> writes:
> When a connection is initialised, calling `SELECT
> current_setting('hasura.user', 't');`, as expected will return a NULL.
> However if you call `SET "hasura.user" TO DEFAULT;`, this will actually
> intitialise the value to an empty string.
That's a side effect of SET causing such a custom GUC to spring
into existence --- with an empty-string default, because there
is no better value.
> This presents a huge problem,
If you don't like it, don't use custom GUCs. They're not officially
supported --- the only reason this is allowed at all is to allow
setting of an extension's GUCs before the extension is loaded.
See
https://www.postgresql.org/docs/current/runtime-config-custom.html
regards, tom lane
Re: BUG #18544: Setting local config_parameter to DEFAULT behaves unexpectedly when using period in config name
From
"David G. Johnston"
Date:
On Thursday, July 18, 2024, Hayden Sim <haydenwillsim@gmail.com> wrote:
I understand it is a side effect of SET causing the custom GUC to be initialised. But the behaviour of `SET LOCAL` affecting the entire session, even outside of the transaction seems bizarre. Should exiting the transaction or calling `SET ... TO DEFAULT` not cause the parameter to be deleted?
Yes, it is a POLA violation. But there is no interest in fixing this, especially not as a bug fix. I suggest you instead support the commitfest patch to get proper variables into PostgreSQL.
To reiterate, it is a bug in client code to rely on NULL being a setting value (i.e., we made a mistake in providing a current setting function that produces null instead of an error.).
There is pending documentation, that probably either needs tweaks or could be modified to update different areas, in light of this discussion (touching current_setting seems warranted) to add this behavior more prominently to the docs. Reviewing that would also help.
David J.