Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 1ae127f

Browse files
committed
Day 49
1 parent 88c69a4 commit 1ae127f

File tree

2 files changed

+251
-0
lines changed

2 files changed

+251
-0
lines changed
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
Originally from: [tweet](https://twitter.com/samokhvalov/status/1724351012562141612), [LinkedIn post]().
2+
3+
---
4+
5+
# How to use variables in psql scripts
6+
7+
> I post a new PostgreSQL "howto" article every day. Join me in this
8+
> journey – [subscribe](https://twitter.com/samokhvalov/), provide feedback, share!
9+
10+
`psql` is a native terminal-based client for PostgreSQL. It is very powerful, available on many platforms, is installed
11+
with Postgres (often in a separate package, e.g., `apt install postgresql-client-16` on Ubuntu/Debian).
12+
13+
`psql` supports advanced scripting, and `psql` scripts can be viewed as a superset of Postgres SQL dialect.
14+
For example, it supports commands like `\set`, `\if`, `\watch`. I usually use extension `.psql` for the scripts that are
15+
to be executed by `psql`.
16+
17+
There are two types of variables that can be used in `psql`:
18+
19+
1. Client-side (`psql`'s) – those that are set using `\set` and accessed using **colon-prefixed names**.
20+
2. Server-side parameters (a.k.a. user-defined GUC) – those that can be set and viewed using SQL queries, using keywords
21+
`SET` and `SHOW` respectively.
22+
23+
## Client-side variables
24+
25+
For a number:
26+
27+
```sql
28+
nik=# \set var1 1.23
29+
30+
nik=# select :var1 as result;
31+
result
32+
--------
33+
1.23
34+
(1 row)
35+
```
36+
37+
Note that `\set` is a client-side (`psql`'s) instruction, it doesn't need a semicolon in the end.
38+
39+
For a string value:
40+
41+
```sql
42+
nik=# \set str1 'Hello, world'
43+
44+
nik=# select :'str1' as hi;
45+
hi
46+
--------------
47+
Hello, world
48+
(1 row)
49+
```
50+
51+
Note the quite a strange syntax – `:'str1'`. It may require some time to memorize.
52+
53+
Another interesting way to set a `psql`'s client-side variable is to use `\gset` instead of closing semicolon:
54+
55+
```sql
56+
nik=# select now() as ts, current_user as usr \gset
57+
58+
nik=# select :'ts', :'usr';
59+
?column? | ?column?
60+
-------------------------------+----------
61+
2023-11-14 00:27:53.615579-08 | nik
62+
(1 row)
63+
```
64+
65+
## Server-side variables
66+
67+
The most common way to set a user-defined GUC is `SET`:
68+
69+
```sql
70+
nik=# set myvars.v1 to 1.23;
71+
SET
72+
73+
nik=# show myvars.v1;
74+
myvars.v1
75+
-----------
76+
1.23
77+
(1 row)
78+
```
79+
80+
Notes:
81+
82+
- These are SQL queries, ending with a semicolon; they can be executed from other clients as well, not only from `psql`.
83+
- Custom GUC should be accompanied by a "_namespace_" (`set v1 = 1.23;` won't work – un-prefixed parameters are
84+
considered as standard GUC, such as `shared_buffers`).
85+
- Working with strings is straightforward (`set myvars.v1 to 'hello';`).
86+
87+
Values defined by using `SET` do not persist – they are present only during the ongoing session (or, if `SET LOCAL` is
88+
used, only during the current transaction). For persistence, use either of these approaches.
89+
90+
1) Cluster-wide:
91+
```sql
92+
nik=# alter system set myvars.v1 to 2;
93+
ALTER SYSTEM
94+
95+
nik=# select pg_reload_conf();
96+
pg_reload_conf
97+
----------------
98+
t
99+
(1 row)
100+
101+
nik=# \c
102+
You are now connected to database "nik" as user "nik".
103+
104+
nik=# show myvars.v1;
105+
myvars.v1
106+
-----------
107+
2
108+
(1 row)
109+
```
110+
111+
Notice the config reload using `pg_reload_conf()` and a reconnection.
112+
113+
2) At database level:
114+
115+
```sql
116+
nik=# alter database nik set myvars.v1 to 3;
117+
ALTER DATABASE
118+
119+
nik=# \c
120+
You are now connected to database "nik" as user "nik".
121+
122+
nik=# show myvars.v1;
123+
myvars.v1
124+
-----------
125+
3
126+
(1 row)
127+
```
128+
129+
3) At user level:
130+
131+
```sql
132+
nik=# alter user nik set myvars.v1 to 4;
133+
ALTER ROLE
134+
135+
nik=# \c
136+
You are now connected to database "nik" as user "nik".
137+
138+
nik=# show myvars.v1;
139+
myvars.v1
140+
-----------
141+
4
142+
(1 row)
143+
```
144+
145+
## Server-side variables – how to integrate with SQL
146+
147+
This `SET`/`SHOW` syntax is very common. However, it is often inconvenient because neither `SET` nor `SHOW` can be
148+
integrated to other SQL queries such as `SELECT`. To solve this, use alternative methods to set and
149+
access – `set_config(...)`
150+
and `current_setting(...)` ([docs](https://postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADMIN-SET)).
151+
152+
Instead of `SET`, use `set_config(...)`:
153+
154+
```sql
155+
nik=# select set_config('myvars.v1', '5', false);
156+
set_config
157+
------------
158+
5
159+
(1 row)
160+
161+
nik=# show myvars.v1;
162+
myvars.v1
163+
-----------
164+
5
165+
(1 row)
166+
```
167+
168+
Note that the value can be text-only – so for numbers and other data types, subsequent conversion may be needed.
169+
170+
And instead of `SHOW`, use `current_setting(...)`:
171+
172+
```sql
173+
nik=# select set_config('myvars.v1', '6', false);
174+
set_config
175+
------------
176+
6
177+
(1 row)
178+
179+
nik=# select current_setting('myvars.v1', true)::int;
180+
current_setting
181+
-----------------
182+
6
183+
(1 row)
184+
```
185+
186+
## Variables in anonymous DO blocks
187+
188+
💡👉 **Idea from
189+
[passing parameters from command line to DO statement](https://postgres.cz/wiki/PostgreSQL_SQL_Tricks#Passing_parameters_from_command_line_to_DO_statement)**.
190+
191+
Anonymous `DO` blocks do not support client-side variables, so we need to pass them to the server-side first:
192+
193+
```sql
194+
nik=# \set loops 5
195+
196+
nik=# select set_config('myvars.loops', (:loops)::text, false);
197+
set_config
198+
------------
199+
5
200+
(1 row)
201+
202+
nik=# do $$
203+
begin
204+
for i in 1..current_setting('myvars.loops', true)::int loop
205+
raise notice 'Iteration %', i;
206+
end loop;
207+
end $$;
208+
NOTICE: Iteration 1
209+
NOTICE: Iteration 2
210+
NOTICE: Iteration 3
211+
NOTICE: Iteration 4
212+
NOTICE: Iteration 5
213+
DO
214+
```
215+
216+
## Passing variables to .psql scripts
217+
218+
Consider that we have a script named `largest_tables.psql`:
219+
220+
```sql
221+
❯ cat largest_tables.psql
222+
223+
select
224+
relname,
225+
pg_total_relation_size(oid::regclass),
226+
pg_size_pretty(pg_total_relation_size(oid::regclass))
227+
from pg_class
228+
order by pg_total_relation_size(oid::regclass) desc
229+
limit :limit;
230+
```
231+
232+
Now, we can call it dynamically by setting the value for client-side variable `limit`:
233+
234+
```sql
235+
❯ psql -X -f largest_tables.psql -v limit=2
236+
relname | pg_total_relation_size | pg_size_pretty
237+
------------------+------------------------+----------------
238+
pgbench_accounts | 164732928 | 157 MB
239+
t13 | 36741120 | 35 MB
240+
(2 rows)
241+
242+
❯ PGAPPNAME=mypsql psql -X \
243+
-f largest_tables.psql \
244+
-v limit=3 \
245+
--csv
246+
relname,pg_total_relation_size,pg_size_pretty
247+
pgbench_accounts,164732928,157 MB
248+
t13,36741120,35 MB
249+
tttttt,36741120,35 MB
250+
```

‎README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ As an example, first 2 rows:
7575
- 0046 [How to deal with bloat](./0046_how_to_deal_with_bloat.md)
7676
- 0047 [How to install Postgres 16 with plpython3u: Recipes for macOS, Ubuntu, Debian, CentOS, Docker](./0047_how_to_install_postgres_16_with_plpython3u.md)
7777
- 0048 [How to generate fake data](./0048_how_to_generate_fake_data.md)
78+
- 0049 [How to use variables in psql scripts](./0049_how_to_use_variables_in_psql_scripts.md)
7879
- ...
7980

8081
## Contributors

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /