如何在 psql 中使用脚本变量?

发布于 2024-07-04 04:21:51 字数 310 浏览 6 评论 0原文

在 MS SQL Server 中,我创建脚本来使用可自定义变量:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

然后,我将在运行时更改 @somevariable 的值,具体取决于特定情况下我想要的值。 由于它位于脚本的顶部,因此很容易看到和记住。

如何使用 PostgreSQL 客户端 psql 执行相同操作?

In MS SQL Server, I create my scripts to use customizable variables:

DECLARE @somevariable int  
SELECT @somevariable = -1

INSERT INTO foo VALUES ( @somevariable )

I'll then change the value of @somevariable at runtime, depending on the value that I want in the particular situation. Since it's at the top of the script it's easy to see and remember.

How do I do the same with the PostgreSQL client psql?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

梦里泪两行 2024-07-11 04:21:52

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 注入的全面保护。

postgres (since version 9.0) allows anonymous blocks in any of the supported server-side scripting languages

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

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

As everything is inside a string, external string variables being substituted in will need to be escaped and quoted twice. Using dollar quoting instead will not give full protection against SQL injection.

蓝眸 2024-07-11 04:21:52

您需要使用一种过程语言,例如 PL/pgSQL,而不是 SQL proc 语言。
在 PL/pgSQL 中,您可以直接在 SQL 语句中使用变量。
对于单引号,您可以使用引用文字函数。

You need to use one of the procedural languages such as PL/pgSQL not the SQL proc language.
In PL/pgSQL you can use vars right in SQL statements.
For single quotes you can use the quote literal function.

撞了怀 2024-07-11 04:21:52

我用临时表解决了这个问题。

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

这样,我就有了一个可以在多个查询中使用的“变量”,该变量对于会话来说是唯一的。 我需要它生成唯一的“用户名”,同时在导入具有相同用户名的用户时仍然不会发生冲突。

I solved it with a temp table.

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

This way, I had a "variable" I could use over multiple queries, that is unique for the session. I needed it to generate unique "usernames" while still not having collisions if importing users with the same user name.

明月松间行 2024-07-11 04:21:52

另一种方法是(ab)使用 PostgreSQL GUC 机制来创建变量。 有关详细信息和示例,请参阅之前的答案

您在 postgresql.conf 中声明 GUC,然后在运行时使用 SET 命令更改其值,并使用 current_setting(...) 获取其值。

我不建议将此用于一般用途,但它在狭窄的情况下可能很有用,例如链接问题中提到的情况,发布者想要一种为触发器和函数提供应用程序级用户名的方法。

Another approach is to (ab)use the PostgreSQL GUC mechanism to create variables. See this prior answer for details and examples.

You declare the GUC in postgresql.conf, then change its value at runtime with SET commands and get its value with current_setting(...).

I don't recommend this for general use, but it could be useful in narrow cases like the one mentioned in the linked question, where the poster wanted a way to provide the application-level username to triggers and functions.

一直在等你来 2024-07-11 04:21:52

我真的很怀念这个功能。 实现类似功能的唯一方法是使用函数。

我以两种方式使用它:

  • 使用 $_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 不是最好的会话标识,请考虑使用 pid 与 pg_stat_activity
  • 这个表 中的 backend_start 结合使用版本也很糟糕,因为您必须偶尔清除此版本(并且不要删除当前正在工作的会话变量)

I really miss that feature. Only way to achieve something similar is to use functions.

I have used it in two ways:

  • perl functions that use $_SHARED variable
  • store your variables in table

Perl version:

   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;

Table version:

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';

Notes:

  • plperlu is faster than perl
  • pg_backend_pid is not best session identification, consider using pid combined with backend_start from pg_stat_activity
  • this table version is also bad because you have to clear this is up occasionally (and not delete currently working session variables)
野鹿林 2024-07-11 04:21:52

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)

Variables in psql suck. If you want to declare an integer, you have to enter the integer, then do a carriage return, then end the statement in a semicolon. Observe:

Let's say I want to declare an integer variable my_var and insert it into a table test:

Example table 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)

Clearly, nothing in this table yet:

thedatabase=# select * from test;
 id 
----
(0 rows)

We declare a variable. Notice how the semicolon is on the next line!

thedatabase=# \set my_var 999
thedatabase=# ;

Now we can insert. We have to use this weird ":''" looking syntax:

thedatabase=# insert into test(id) values (:'my_var');
INSERT 0 1

It worked!

thedatabase=# select * from test;
 id  
-----
 999
(1 row)

Explanation:

So... what happens if we don't have the semicolon on the next line? The variable? Have a look:

We declare my_var without the new line.

thedatabase=# \set my_var 999;

Let's select my_var.

thedatabase=# select :'my_var';
 ?column? 
----------
 999;
(1 row)

WTF is that? It's not an integer, it's a string 999;!

thedatabase=# select 999;
 ?column? 
----------
      999
(1 row)
沒落の蓅哖 2024-07-11 04:21:52

我为此发布了一个新的解决方案 在另一个线程上

它使用表来存储变量,并且可以随时更新。 静态不可变 getter 函数是动态创建的(由另一个函数),由表更新触发。 您将获得良好的表存储,以及不可变 getter 的极快速度。

I've posted a new solution for this on another thread.

It uses a table to store variables, and can be updated at any time. A static immutable getter function is dynamically created (by another function), triggered by update to your table. You get nice table storage, plus the blazing fast speeds of an immutable getter.

独自←快乐 2024-07-11 04:21:52

我发现这个问题和答案非常有用,但也令人困惑。 我在让带引号的变量工作时遇到了很多麻烦,所以这是我让它工作的方法:

\set deployment_user username    -- username
\set deployment_pass '\'string_password\''
ALTER USER :deployment_user WITH PASSWORD :deployment_pass;

这样您就可以在一个语句中定义变量。 当您使用它时,单引号将嵌入到变量中。

笔记! 当我在引用的变量后面添加注释时,当我尝试其他答案中的某些方法时,它会作为变量的一部分被吸收。 这确实让我搞砸了一段时间。 使用此方法,注释似乎会按照您的预期进行处理。

I've found this question and the answers extremely useful, but also confusing. I had lots of trouble getting quoted variables to work, so here is the way I got it working:

\set deployment_user username    -- username
\set deployment_pass '\'string_password\''
ALTER USER :deployment_user WITH PASSWORD :deployment_pass;

This way you can define the variable in one statement. When you use it, single quotes will be embedded into the variable.

NOTE! When I put a comment after the quoted variable it got sucked in as part of the variable when I tried some of the methods in other answers. That was really screwing me up for a while. With this method comments appear to be treated as you'd expect.

各自安好 2024-07-11 04:21:51

FWIW,真正的问题是我在 \set 命令末尾包含了一个分号:

\setowner_password 'thepassword';

分号被解释为变量中的实际字符:

\echo :所有者密码
密码;

所以当我尝试使用它时:

创建角色 myrole 登录未加密密码:owner_password NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'infinity';

...我懂了:

创建角色 myrole 登录未加密密码 thepassword; NOINHERIT CREATEDB CREATEROLE 直到“无穷大”为止有效;

这不仅未能在文字周围设置引号,而且将命令分为两部分(第二部分无效,因为它以“NOINHERIT”开头)。

这个故事的寓意是:PostgreSQL“变量”实际上是文本扩展中使用的宏,而不是真正的值。 我确信这会派上用场,但一开始会很棘手。

FWIW, the real problem was that I had included a semicolon at the end of my \set command:

\set owner_password 'thepassword';

The semicolon was interpreted as an actual character in the variable:

\echo :owner_password
thepassword;

So when I tried to use it:

CREATE ROLE myrole LOGIN UNENCRYPTED PASSWORD :owner_password NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'infinity';

...I got this:

CREATE ROLE myrole LOGIN UNENCRYPTED PASSWORD thepassword; NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'infinity';

That not only failed to set the quotes around the literal, but split the command into 2 parts (the second of which was invalid as it started with "NOINHERIT").

The moral of this story: PostgreSQL "variables" are really macros used in text expansion, not true values. I'm sure that comes in handy, but it's tricky at first.

一杆小烟枪 2024-07-11 04:21:51

关于 PSQL 变量的最后一句话:

  1. 如果您在 SQL 语句中将它们用单引号引起来,它们就不会扩展。
    因此这不起作用:

    SELECT * FROM foo WHERE bar = ':myvariable' 
      
  2. 要在 SQL 语句中扩展为字符串文字,您必须在变量集中包含引号。 但是,变量值已经必须用引号引起来,这意味着您需要第二组引号,并且内部组必须被转义。 因此你需要:

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

    编辑:从 PostgreSQL 9.1 开始,您可以改为编写:

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

One final word on PSQL variables:

  1. They don't expand if you enclose them in single quotes in the SQL statement.
    Thus this doesn't work:

    SELECT * FROM foo WHERE bar = ':myvariable'
    
  2. To expand to a string literal in a SQL statement, you have to include the quotes in the variable set. However, the variable value already has to be enclosed in quotes, which means that you need a second set of quotes, and the inner set has to be escaped. Thus you need:

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

    EDIT: starting with PostgreSQL 9.1, you may write instead:

    \set myvariable somestring
    SELECT * FROM foo WHERE bar = :'myvariable'
    
幻想少年梦 2024-07-11 04:21:51

您可以尝试使用 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;

You can try to use a WITH clause.

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;
有深☉意 2024-07-11 04:21:51

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;

Postgres variables are created through the \set command, for example ...

\set myvariable value

... and can then be substituted, for example, as ...

SELECT * FROM :myvariable.table1;

... or ...

SELECT * FROM table1 WHERE :myvariable IS NULL;

edit: As of psql 9.1, variables can be expanded in quotes as in:

\set myvariable value 

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

In older versions of the psql client:

... If you want to use the variable as the value in a conditional string query, such as ...

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

... then you need to include the quotes in the variable itself as the above will not work. Instead define your variable as such ...

\set myvariable 'value'

However, if, like me, you ran into a situation in which you wanted to make a string from an existing variable, I found the trick to be this ...

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

Now you have both a quoted and unquoted variable of the same string! And you can do something like this ....

INSERT INTO :myvariable.table1 SELECT * FROM table2 WHERE column1 = :quoted_myvariable;
原野 2024-07-11 04:21:51

特别是对于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,但不适用于 -c / --command; 您必须通过 stdin 或通过 -f 发送命令。 它在(例如)PgAdmin-III 中也不起作用。

这种替换发生在 psql 的输入处理过程中,因此您不能(比如说)定义使用 :'filepath' 的函数并期望 :'filepath' 的值从一个会话到另一个会话的更改。 当函数被定义时,它将被替换一次,然后将是一个常量。 它对于脚本编写很有用,但对于运行时使用却没有用。

Specifically for psql, you can pass psql variables from the command line too; you can pass them with -v. Here's a usage example:

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

Note that the colon is unquoted, then the variable name its self is quoted. Odd syntax, I know. This only works in psql but doesn't work with -c / --command; you have to send the command via stdin or via -f. It won't work in (say) PgAdmin-III too.

This substitution happens during input processing in psql, so you can't (say) define a function that uses :'filepath' and expect the value of :'filepath' to change from session to session. It'll be substituted once, when the function is defined, and then will be a constant after that. It's useful for scripting but not runtime use.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文