Перевод: CYBERTEC: gexec в psql: Опыт продвинутых пользователей PostgreSQL

PostgreSQLCYBERTEC опять интересную статью опубликовали, не всё из нее мне было известно — поэтому читать было интересно.

Ссылка на оригинал: gexec in psql: PostgreSQL poweruser practice


Автоматизация работы становится для опытных пользователей PostgreSQL всё более необходимой. И команда gexec может в этом помочь. Эта статья покажет как использовать оператор конкатенации || и команду \gexec и избежать ненужных повторений лишних действий.

Инструмент командной строки (CLI), поставляемый с PostgreSQL, называется psql. Как и многие другие CLI-клиенты, про psql сразу же забывают после установки сервера и заменяют его на какую-нибудь графическую утилиту управления. Или используют psql только для простых операций, выполняя более сложные с помощью других средств. Однако psql — очень мощный инструмент с большим количеством полезных функций.

Периодически бывает необходимо запускать одинаковую команду с разными параметрами. Чаще всего пользователи переписывают команду снова и снова. Или могут один раз написать команду в текстовом редакторе, а потом копировать её, вставлять и менять только параметры.

Иногда может быть полезно автоматизировать такие шаги — не только для того, чтобы сократить время, но и чтобы избежать ошибок при вставке или копировании. PostgreSQL может получить результаты запросов и добавить полученный текст в новую команду в качестве параметров.

Для этого мы можем добавить в начало или конец любого результата запроса произвольный текст с помощью оператора ||

Упражнение 1: использование оператора ||

Представим что новому пользователю нужен доступ к некоторым таблицам в схеме, то есть ко всем таблицам, которые имеют один и тот же префикс.

Мы можем сделать это вручную или попросить базу данных автоматизировать это.

1. Давайте получим таблицы с именами, начинающимися на pgbench

postgres=# SELECT tablename FROM pg_tables WHERE tablename~'^pgbench';
tablename
------------------
pgbench_accounts
pgbench_branches
pgbench_history
pgbench_tellers
(4 rows)

2. А теперь воспользуемся || для добавления начального и конечного фрагментов чтобы создать корректную команду с именем таблицы в качестве параметра.

postgres=# SELECT 'GRANT SELECT ON TABLE ' || tablename || ' TO someuser;' FROM pg_tables WHERE tablename~'^pgbench';
?column?
-----------------------------------------------------
GRANT SELECT ON TABLE pgbench_accounts TO someuser;
GRANT SELECT ON TABLE pgbench_branches TO someuser;
GRANT SELECT ON TABLE pgbench_history TO someuser;
GRANT SELECT ON TABLE pgbench_tellers TO someuser;
(4 rows)

Обратите внимание, что строки заканчиваются или начинаются с дополнительных пробелов так как имя таблицы не содержит необходимых пробелов для разделения аргументов. Символ точки с запятой ; тоже был добавлен чтобы команды можно было сразу выполнять.

Имейте в виду что, несмотря на удобство использования || для объединения (конкатенации) текста, это не считается хорошей практикой, поскольку он уязвим для SQL-инъекций, как показано в комментарии ниже:

НЕ объединяйте слепо имена таблиц с запросами. Вместо этого используйте quote_ident() или format() с %I. Они применяют правильное экранирование по мере необходимости.

Более безопасный подход для получения тех же результатов будет примерно таким:

postgres=# SELECT format('GRANT SELECT ON TABLE %I TO someuser;', tablename) FROM pg_tables WHERE tablename~'^pgbench';
format
-----------------------------------------------------
GRANT SELECT ON TABLE pgbench_accounts TO someuser;
GRANT SELECT ON TABLE pgbench_branches TO someuser;
GRANT SELECT ON TABLE pgbench_history TO someuser;
GRANT SELECT ON TABLE pgbench_tellers TO someuser;
(4 rows)

Теперь эти команды можно скопировать, а затем вставить прямо в строку приглашения (???).

Я даже видел, как люди берут такие строки, сохраняют их в файл, а затем заставляют psql выполнять все команды из файла.

Но, к счастью, существует способ намного проще.

\gexec

В psql есть много способов и внутренних инструментов для быстрого сбора информации о базах данных, схемах, таблицах, привилегиях и многом другом.

psql позволяет работать с входными и выходными буферами, и это свойство можно использовать вместе с \gexec чтобы psql выполнял каждую команду из выходного буфера.

Упражнение 2: вызов \gexec

Вернувшись к запросу для генерации необходимых команд, мы можем вызвать \gexec для выполнения каждой строки из вывода запроса.

postgres=# SELECT 'GRANT SELECT ON TABLE ' || tablename || ' TO someuser;' FROM pg_tables WHERE tablename~'^pgbench';
?column?
-----------------------------------------------------
GRANT SELECT ON TABLE pgbench_accounts TO someuser;
GRANT SELECT ON TABLE pgbench_branches TO someuser;
GRANT SELECT ON TABLE pgbench_history TO someuser;
GRANT SELECT ON TABLE pgbench_tellers TO someuser;
(4 rows)

postgres=# \gexec
GRANT
GRANT
GRANT
GRANT

Упражнение 3: CROSS JOIN с \gexec

Предполагая, что вы хотите использовать большое количество аргументов, вы всегда можете добавить ещё операторов || чтобы добавить больше фрагментов команд вокруг результатов запроса.

Предположим, вам необходимо предоставить права на вставку, обновление и удаление из этих таблиц.

Простой CROSS JOIN дает нам желаемый результат (с помощью набора строк, построенного с использованием конструктора VALUES) для каждого из имен таблиц.

postgres=# SELECT action, tablename FROM pg_tables CROSS JOIN (VALUES ('INSERT'),('UPDATE'),('DELETE')) AS t(action) WHERE tablename~'^pgbench';
action | tablename
--------+------------------
INSERT | pgbench_accounts
UPDATE | pgbench_accounts
DELETE | pgbench_accounts
INSERT | pgbench_branches
UPDATE | pgbench_branches
DELETE | pgbench_branches
INSERT | pgbench_history
UPDATE | pgbench_history
DELETE | pgbench_history
INSERT | pgbench_tellers
UPDATE | pgbench_tellers
DELETE | pgbench_tellers
(12 rows)

Обратите внимание, что мы явно назначаем имя столбца «action» с помощью AS t(action) таблице, сгенерированной с помощью VALUES.

postgres=# SELECT 'GRANT ' || action || ' ON TABLE ' || tablename || ' TO someuser;' FROM pg_tables CROSS JOIN (VALUES ('INSERT'),('UPDATE'),('DELETE')) AS t(action) WHERE tablename~'^pgbench';
?column?
-----------------------------------------------------
GRANT INSERT ON TABLE pgbench_accounts TO someuser;
GRANT UPDATE ON TABLE pgbench_accounts TO someuser;
GRANT DELETE ON TABLE pgbench_accounts TO someuser;
GRANT INSERT ON TABLE pgbench_branches TO someuser;
GRANT UPDATE ON TABLE pgbench_branches TO someuser;
GRANT DELETE ON TABLE pgbench_branches TO someuser;
GRANT INSERT ON TABLE pgbench_history TO someuser;
GRANT UPDATE ON TABLE pgbench_history TO someuser;
GRANT DELETE ON TABLE pgbench_history TO someuser;
GRANT INSERT ON TABLE pgbench_tellers TO someuser;
GRANT UPDATE ON TABLE pgbench_tellers TO someuser;
GRANT DELETE ON TABLE pgbench_tellers TO someuser;
(12 rows)

Далее вывод этого запроса опять можно выполнить с помощью \gexec.

postgres=# \gexec
GRANT
GRANT
GRANT
GRANT
GRANT
GRANT
GRANT
GRANT
GRANT
GRANT
GRANT
GRANT

Упражнение 4: добавление кавычек

В зависимости от обстоятельств может потребоваться добавить в вывод дополнительные кавычки, например, когда имена таблиц содержат заглавные буквы или пробелы. В таких случаях соответствующие двойные кавычки могут быть добавлены к строкам до и после аргументов.

postgres=# SELECT 'GRANT SELECT ON TABLE "' || tablename || '" TO someuser;' FROM pg_tables WHERE schemaname='public';
?column?
-----------------------------------------------------
GRANT SELECT ON TABLE "with spaces" TO someuser;
GRANT SELECT ON TABLE "Capitalization" TO someuser;
GRANT SELECT ON TABLE "capitalization" TO someuser;
(3 rows)

postgres=# \gexec
GRANT
GRANT
GRANT

Теперь, когда вы знаете, как использовать \gexec, почему бы не сделать следующий шаг? Посмотрите нашу статью о том, что порядке столбцов в PostgreSQL имеет значение, чтобы увидеть использование \gexec на другом практическом примере.

Если вы хотите узнать больше о безопасности в PostgreSQL, смотрите мою статью о Безопасности транспортного уровня.

Julian Markwort

Джулиан Маркворт (Julian Markwort)

Джулиан Маркворт — младший консультант и разработчик в CYBERTEC. Его участие в сообществе PostgreSQL началось примерно в 2015 году, когда Джулиан работал студентом в Мюнстерском университете, где он продолжает обучение на магистра наук, на всякий случай и, одновременно, помогая глобальным корпорациям запускать кластеры Patroni, а также разрабатывать и создавать много полезного, связанного с базами данных.


Ещё раз ссылка на оригинал: gexec in psql: PostgreSQL poweruser practice


Be the first to comment

Leave a Reply

Ваш Mail не будет опубликован.


*