Как вы используете скриптовые переменные в PSQL? - PullRequest
114 голосов
/ 31 августа 2008

В MS SQL Server я создаю свои скрипты для использования настраиваемых переменных:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

Затем я изменю значение @somevariable во время выполнения, в зависимости от значения, которое я хочу в конкретной ситуации. Поскольку он находится в верхней части скрипта, его легко увидеть и запомнить.

Как мне сделать то же самое с клиентом PostgreSQL psql?

Ответы [ 13 ]

163 голосов
/ 28 августа 2010

Переменные Postgres создаются с помощью команды \ set, например ...

\set myvariable value

... и затем может быть заменен, например, как ...

SELECT * FROM :myvariable.table1;

... или ...

SELECT * FROM table1 WHERE :myvariable IS NULL;

edit: Начиная с psql 9.1, переменные можно расширять в кавычки, как в:

\set myvariable value 

SELECT * FROM table1 WHERE column1 = :'myvariable';

В старых версиях клиента psql:

... Если вы хотите использовать переменную в качестве значения в запросе условной строки, например ...

SELECT * FROM table1 WHERE column1 = ':myvariable';

... тогда вам нужно включить кавычки в саму переменную, так как вышеописанное не будет работать. Вместо этого определите вашу переменную как таковую ...

\set myvariable 'value'

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

\set quoted_myvariable '\'' :myvariable '\''

Теперь у вас есть переменная в кавычках и без кавычек в одной строке! И вы можете сделать что-то вроде этого ...

INSERT INTO :myvariable.table1 SELECT * FROM table2 WHERE column1 = :quoted_myvariable;
57 голосов
/ 31 августа 2008

Последнее слово для переменных PSQL:

  1. Они не раскрываются, если вы заключите их в одинарные кавычки в операторе SQL. Таким образом, это не работает:

    SELECT * FROM foo WHERE bar = ':myvariable'
    
  2. Чтобы раскрыть строковый литерал в операторе SQL, необходимо включить кавычки в набор переменных. Однако значение переменной уже должно быть заключено в кавычки, что означает, что вам нужен набор second кавычек, и внутренний набор должен быть экранирован Таким образом вам нужно:

    \set myvariable '\'somestring\''  
    SELECT * FROM foo WHERE bar = :myvariable
    

    РЕДАКТИРОВАТЬ : начиная с PostgreSQL 9.1, вы можете написать вместо этого:

    \set myvariable somestring
    SELECT * FROM foo WHERE bar = :'myvariable'
    
40 голосов
/ 08 марта 2013

Вы можете попробовать использовать предложение WITH .

WITH vars AS (SELECT 42 AS answer, 3.14 AS appr_pi)
SELECT t.*, vars.answer, t.radius*vars.appr_pi
FROM table AS t, vars;
28 голосов
/ 10 ноября 2012

Специально для psql вы также можете передавать psql переменные из командной строки; Вы можете передать их с -v. Вот пример использования:

$ psql -v filepath=/path/to/my/directory/mydatafile.data regress
regress=> SELECT :'filepath';
               ?column?                
---------------------------------------
 /path/to/my/directory/mydatafile.data
(1 row)

Заметьте, что двоеточие не заключено в кавычки, тогда имя переменной его self заключено в кавычки. Странный синтаксис, я знаю. Это работает только в psql; это не будет работать, скажем, в PgAdmin-III.

Эта замена происходит во время обработки ввода в psql, поэтому вы не можете (скажем) определить функцию, которая использует :'filepath' и ожидать, что значение :'filepath' изменится от сеанса к сеансу. Он будет подставлен один раз, когда функция будет определена, и после этого станет константой. Это полезно для сценариев, но не для использования во время выполнения.

12 голосов
/ 22 сентября 2008

Вам необходимо использовать один из процедурных языков, таких как PL / pgSQL, а не язык процедур SQL. В PL / pgSQL вы можете использовать переменные прямо в инструкциях SQL. Для одинарных кавычек вы можете использовать буквальную функцию quote.

12 голосов
/ 31 августа 2008

FWIW, реальная проблема заключалась в том, что я добавил точку с запятой в конце моей команды \ set:

\ set owner_password 'thepassword';

Точка с запятой была интерпретирована как фактический символ в переменной:

\ echo: owner_password thepassword;

Итак, когда я попытался его использовать:

СОЗДАТЬ РОЛЬ myrole ВХОД НЕПИСАННЫЙ ПАРОЛЬ: owner_password NOINHERIT CREATEDB CREATEROLE ДЕЙСТВИТЕЛЬНО ДО 'infinity';

... Я получил это:

СОЗДАТЬ РОЛЬ myrole ВХОД НЕПИСАННЫЙ ПАРОЛЬ thepassword; NOINHERIT CREATEDB CREATEROLE ДЕЙСТВИТЕЛЬНО ДО 'Infinity';

Это не только не позволило установить кавычки вокруг литерала, но и разделило команду на 2 части (вторая из которых была недействительной, поскольку начиналась с "NOINHERIT").

Мораль этой истории: переменные PostgreSQL - это действительно макросы, используемые в раскрытии текста, а не истинные значения. Я уверен, что это пригодится, но сначала это сложно.

9 голосов
/ 11 мая 2016

postgres (начиная с версии 9.0) разрешает анонимные блоки на любом из поддерживаемых серверных языков сценариев

DO '
DECLARE somevariable int = -1;
BEGIN
INSERT INTO foo VALUES ( somevariable );
END
' ;

http://www.postgresql.org/docs/current/static/sql-do.html

Поскольку все находится внутри строки, внешние строковые переменные, которые должны быть подставлены, необходимо экранировать и заключать в кавычки дважды. Использование долларовых кавычек вместо этого не даст полной защиты от SQL-инъекций.

5 голосов
/ 10 ноября 2012

Другой подход состоит в том, чтобы (ab) использовать механизм PostgreSQL GUC для создания переменных. См. этот предыдущий ответ для деталей и примеров.

Вы объявляете GUC в postgresql.conf, затем изменяете его значение во время выполнения с помощью команд SET и получаете его с помощью current_setting(...).

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

3 голосов
/ 27 октября 2014

Я решил это с помощью временной таблицы.

CREATE TEMP TABLE temp_session_variables (
    "sessionSalt" TEXT
);
INSERT INTO temp_session_variables ("sessionSalt") VALUES (current_timestamp || RANDOM()::TEXT);

Таким образом, у меня была «переменная», которую я мог использовать для нескольких запросов, уникальная для сеанса. Мне нужно было генерировать уникальные «имена пользователей», но при этом не было коллизий при импорте пользователей с одинаковыми именами.

2 голосов
/ 20 декабря 2012

Я очень скучаю по этой функции. Единственный способ добиться чего-то подобного - использовать функции.

Я использовал его двумя способами:

  • Perl-функции, которые используют переменную $ _SHARED
  • сохранить ваши переменные в таблице

Версия Perl:

   CREATE FUNCTION var(name text, val text) RETURNS void AS $$
        $_SHARED{$_[0]} = $_[1];
   $$ LANGUAGE plperl;
   CREATE FUNCTION var(name text) RETURNS text AS $$
        return $_SHARED{$_[0]};
   $$ LANGUAGE plperl;

Настольная версия:

CREATE TABLE var (
  sess bigint NOT NULL,
  key varchar NOT NULL,
  val varchar,
  CONSTRAINT var_pkey PRIMARY KEY (sess, key)
);
CREATE FUNCTION var(key varchar, val anyelement) RETURNS void AS $$
  DELETE FROM var WHERE sess = pg_backend_pid() AND key = $1;
  INSERT INTO var (sess, key, val) VALUES (sessid(), $1, $2::varchar);
$$ LANGUAGE 'sql';

CREATE FUNCTION var(varname varchar) RETURNS varchar AS $$
  SELECT val FROM var WHERE sess = pg_backend_pid() AND key = $1;
$$ LANGUAGE 'sql';

Примечания:

  • plperlu быстрее, чем perl
  • pg_backend_pid - не лучшая идентификация сессии, рассмотрите использование pid в сочетании с backend_start из pg_stat_activity
  • эта версия таблицы также плоха, потому что вы должны периодически очищать ее (а не удалять текущие переменные сеанса)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...