psql でスクリプト変数をどのように使用しますか?
-
09-06-2019 - |
質問
MS SQL Server では、カスタマイズ可能な変数を使用するスクリプトを作成します。
DECLARE @somevariable int
SELECT @somevariable = -1
INSERT INTO foo VALUES ( @somevariable )
次に、の値を変更します @somevariable
特定の状況で必要な値に応じて、実行時に。スクリプトの先頭にあるので、見やすく覚えやすいです。
PostgreSQL クライアントで同じことを行うにはどうすればよいですか psql
?
解決
Postgres 変数は、\set コマンドによって作成されます。たとえば、...
\set myvariable value
...そして、たとえば ... のように置き換えることができます。
SELECT * FROM :myvariable.table1;
...または ...
SELECT * FROM table1 WHERE :myvariable IS NULL;
編集: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;
他のヒント
PSQL 変数について最後に一言:
SQL ステートメント内で単一引用符で囲んだ場合は展開されません。したがって、これは機能しません:
SELECT * FROM foo WHERE bar = ':myvariable'
SQL ステートメント内の文字列リテラルに展開するには、変数セットに引用符を含める必要があります。ただし、変数値はすでに引用符で囲む必要があります。つまり、 2番 引用符のセットであり、内側のセットはエスケープする必要があります。したがって、次のものが必要です。
\set myvariable '\'somestring\'' SELECT * FROM foo WHERE bar = :myvariable
編集:PostgreSQL 9.1 以降では、代わりに次のように記述できます。
\set myvariable somestring SELECT * FROM foo WHERE bar = :'myvariable'
を使用してみることができます と 句。
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;
特に 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)
コロンが引用符で囲まれていない場合、変数名自体が引用符で囲まれていることに注意してください。奇妙な構文ですね、私は知っています。これは psql でのみ機能します。(たとえば) PgAdmin-III では機能しません。
この置換は psql での入力処理中に発生するため、次のような関数を定義することはできません。 :'filepath'
の値を期待します :'filepath'
セッションごとに変更します。関数の定義時に一度置換され、その後は定数になります。これはスクリプト作成には便利ですが、ランタイムには便利ではありません。
FWIW、本当の問題は、\set コマンドの最後にセミコロンを含めたことでした。
\set owner_password 'パスワード';
セミコロンは変数内の実際の文字として解釈されました。
echo:owner_password thePassword;
それで私がそれを使おうとしたとき:
CREATE ROLE myrole LOGIN UNENCRYPTED PASSWORD :owner_password NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'infinity';
...私はこれを得た:
ロールを作成 myrole ログイン 暗号化されていないパスワード パスワード;NOINHERIT CREATEDB CREATEROLE は 'infinity' まで有効です。
これにより、リテラルの周囲に引用符を設定できなかっただけでなく、コマンドが 2 つの部分に分割されました (2 番目の部分は「NOINHERIT」で始まっているため無効でした)。
この話の教訓:PostgreSQL の「変数」は実際にはテキスト展開に使用されるマクロであり、真の値ではありません。確かに便利ですが、最初は難しいです。
SQL proc 言語ではなく、PL/pgSQL などの手続き型言語のいずれかを使用する必要があります。PL/pgSQL では、SQL ステートメント内で直接 vars を使用できます。一重引用符の場合は、引用リテラル関数を使用できます。
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
すべてが文字列内にあるため、置換される外部文字列変数はエスケープして 2 回引用符で囲む必要があります。代わりにドル引用符を使用しても、SQL インジェクションに対する完全な保護は得られません。
もう 1 つのアプローチは、PostgreSQL GUC メカニズムを(アブ)使用して変数を作成することです。見る この前の回答 詳細と例については、
GUC を宣言するのは、 postgresql.conf
, 、実行時に値を変更します。 SET
コマンドを実行し、その値を取得します current_setting(...)
.
これは一般的な使用にはお勧めしませんが、リンクされた質問で言及されているような狭いケースでは役立つ可能性があります。投稿者は、アプリケーションレベルのユーザー名をトリガーと関数に提供する方法を必要としていました。
一時テーブルで解決しました。
CREATE TEMP TABLE temp_session_variables (
"sessionSalt" TEXT
);
INSERT INTO temp_session_variables ("sessionSalt") VALUES (current_timestamp || RANDOM()::TEXT);
このようにして、複数のクエリで使用できる、セッションに固有の「変数」を用意しました。同じユーザー名を持つユーザーをインポートする場合に衝突が発生しないようにしながら、一意の「ユーザー名」を生成するために必要でした。
この質問と回答は非常に便利ですが、混乱を招くものでもあります。引用符で囲まれた変数を機能させるのに多くの困難があったので、これを機能させる方法を次に示します。
\set deployment_user username -- username
\set deployment_pass '\'string_password\''
ALTER USER :deployment_user WITH PASSWORD :deployment_pass;
このようにして、1 つのステートメントで変数を定義できます。これを使用すると、変数に一重引用符が埋め込まれます。
注記!引用符で囲まれた変数の後にコメントを入れると、他の回答でいくつかの方法を試したときに変数の一部として取り込まれました。それはしばらく私を本当に混乱させました。このメソッドを使用すると、コメントは期待どおりに処理されるように見えます。
あの機能が本当に恋しいです。同様のことを実現する唯一の方法は、関数を使用することです。
私はこれを 2 つの方法で使用しました。
- $_SHARED 変数を使用する Perl 関数
- 変数をテーブルに保存します
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 はセッション識別に最適ではありません。pg_stat_activity の backend_start と pid を組み合わせて使用することを検討してください。
- このテーブルのバージョンも不適切です。これは、時々クリアする必要があるためです(現在動作しているセッション変数は削除しないでください)。
の変数 psql
吸う。整数を宣言する場合は、整数を入力し、キャリッジ リターンを実行してからステートメントをセミコロンで終了する必要があります。観察する:
整数変数を宣言したいとします。 my_var
それをテーブルに挿入します test
:
テーブルの例 test
:
thedatabase=# \d test;
Table "public.test"
Column | Type | Modifiers
--------+---------+---------------------------------------------------
id | integer | not null default nextval('test_id_seq'::regclass)
Indexes:
"test_pkey" PRIMARY KEY, btree (id)
明らかに、このテーブルにはまだ何もありません。
thedatabase=# select * from test;
id
----
(0 rows)
変数を宣言します。 次の行にセミコロンがあることに注目してください。
thedatabase=# \set my_var 999
thedatabase=# ;
これで挿入できるようになりました。この奇妙なものを使わなければなりません」:''
" 構文を探しています:
thedatabase=# insert into test(id) values (:'my_var');
INSERT 0 1
出来た!
thedatabase=# select * from test;
id
-----
999
(1 row)
説明:
それで...次の行にセミコロンがない場合はどうなりますか?変数は?ご覧ください:
私たちは宣言します my_var
改行なしで。
thedatabase=# \set my_var 999;
選択しましょう my_var
.
thedatabase=# select :'my_var';
?column?
----------
999;
(1 row)
あれは何だよ?それはありません 整数, 、 それは 弦 999;
!
thedatabase=# select 999;
?column?
----------
999
(1 row)
これに対する新しい解決策を投稿しました 別のスレッドで.
テーブルを使用して変数を保存し、いつでも更新できます。静的で不変のゲッター関数は、テーブルの更新によってトリガーされ、(別の関数によって) 動的に作成されます。優れたテーブル ストレージに加えて、不変ゲッターの非常に高速な速度が得られます。