DO脚本中的PSQL命令行参数

时间:2016-07-03 21:29:28

标签: postgresql command-line parameter-passing plpgsql psql

我有一个脚本NewSchemaSafe.sql,它根据项目目录创建一个新架构;它从Windows命令行调用如下:

for %%a in (.) do set this=%%~na
-- other stuff here
psql -U postgres -d SLSM -e -v v1=%this% -f "NewSchemaSafe.sql"

NewSchemaSafe.sql如下:

-- NewSchemaSafe.sql
-- NEW SCHEMA SETUP 
--    - checks if schema exists
--    - if yes, renames existing with current monthyear as suffix
-- NOTE: will always delete any schema with the 'rename' name (save_schema)
--       since any schema thus named must have resulted from this script 
--       on this date - so, y'know, no loss.
SET search_path TO :v1, public; -- kludge coz can't pass :v1 to DO
DO
$$
DECLARE
       this_schema TEXT:= current_schema()::TEXT;
       this_date TEXT:= replace(current_date::TEXT,'-','');
       save_schema TEXT:= this_schema||this_date;
BEGIN
    IF this_schema <> 'public'
    THEN
        RAISE NOTICE 'Working in schema %', this_schema;
        IF EXISTS(
            SELECT schema_name
              FROM information_schema.schemata
              WHERE schema_name = save_schema)
        THEN
           EXECUTE 'DROP SCHEMA '||save_schema||' CASCADE;';
        END IF;
        IF NOT EXISTS(
            SELECT schema_name
              FROM information_schema.schemata
              WHERE schema_name = this_schema
          )
        THEN
          EXECUTE 'CREATE SCHEMA '||this_schema||';';
        ELSE
          EXECUTE 'ALTER SCHEMA '||this_schema|| ' RENAME TO '|| save_schema ||';';
          EXECUTE 'COMMENT ON SCHEMA '|| save_schema ||' IS ''schema renamed by SLSM creation on '|| this_date ||'''';
          EXECUTE 'CREATE SCHEMA '||this_schema||';';
        END IF;
    ELSE
        RAISE NOTICE 'SCHEMA IS % SO PARAMETER WAS NOT PASSED OR DID NOT STICK', this_schema;
    END IF;
END
$$;

现在我知道SET发生了,因为我可以在命令行输出中看到它。然而,脚本的其余部分(按照预期优雅地)会死掉,因为它似乎认为current_schemapublic:脚本产生

psql: NewSchemaSafe.sql:39: NOTICE:  SCHEMA IS public SO PARAMETER WAS NOT PASSED OR DID NOT STICK

我最初尝试将:v1传递到DECLARE循环的DO块,如下所示:

 DECLARE
       this_schema text := :v1 ;
       this_date text := replace(current_date::text,'-','');
       save_schema text := this_schema||this_date;
  [snip]

但那只是在藤上死了:它会引发语法错误 -

psql:NewSchemaSafe.sql:40: ERROR:  syntax error at or near ":"
LINE 4:        this_schema text := :v1 ;

如果%this%括在引号中或不在批处理文件中,则没有区别。

像往常一样,有两个问题:

  1. set search path陈述怎么会在我能够做到的时候“坚持不懈” 它正在执行?更新:不相关,请忽略。
  2. 如何将:v1参数传递给DO脚本本身?
  3. 环境:PostgreSQL 9.3.5 64位(Win);

    奇怪:我确信这个脚本在两天前工作,唯一的变化就是删除geany插入的字节顺序标记(UTF BOMs make psql gag)。

    更新:前几天工作的原因是它正在考虑的架构的情况下运行。如果作为search_path 传递的架构名称不存在,则更改current_schema(尝试从:v1尝试并获取所需架构)将无法提供帮助 - 这使:v1传递给DO更为重要,因此可以更直接地使用它。

2 个答案:

答案 0 :(得分:2)

因为PL块实际上是代码中的文本常量,所以内部变量不会以通常的方式替换它们。幸运的是,可以使用会话变量在不同的SQL / PL块之间共享数据:

labs(x = 'Sex', y = 'Age Mean', title = 'Suicide 2003-2013 Age Mean by Sex') +
  theme(plot.title = element_text(family = 'Helvetica', 
                              color = '#666666', 
                              face = 'bold', 
                              size = 18, 
                              hjust = 0))

答案 1 :(得分:2)

创建临时函数,而不是使用DO语句。如果您需要传递参数,那就是 解决方案

CREATE FUNCTION pg_temp.f_create_schema(_schema text)  -- note function schema "pg_temp"
  RETURNS void AS 
$func$
DECLARE
   _date        text := to_char(current_date, 'YYYYMMDD');
   _save_schema text := _schema || _date;  -- unescaped identifier
BEGIN
   IF EXISTS (SELECT 1 FROM information_schema.schemata
              WHERE  schema_name = _save_schema) THEN  -- unescaped identifier
      EXECUTE format('DROP SCHEMA %I CASCADE', _save_schema);  -- escaped identifier!
   END IF;

   IF EXISTS (SELECT 1 FROM information_schema.schemata
              WHERE  schema_name = _schema) THEN
      EXECUTE format(
        'ALTER SCHEMA %1$I RENAME TO %2$I;
         COMMENT ON SCHEMA %2$I IS $c$Schema renamed by SLSM creation on %3$s.$c$'
       , _schema, _save_schema, _date);
   END IF;

   EXECUTE 'CREATE SCHEMA ' || quote_ident(_schema);
END
$func$ LANGUAGE plpgsql;

呼叫:

SELECT pg_temp.f_create_schema('Foo');  -- function name must be schema-qualified

来自带有SQL interpolation的psql使用变量v1

SELECT pg_temp.f_create_schema(:'v1');

您为_schema传递的架构名称是区分大小写未加引号

pg_temp是一个伪名称,可在内部自动转换为当前会话的临时模式。临时架构中的所有对象都会在会话的 结尾

“临时”功能未在手册中明确记录,但可以安全使用。

如果您需要在同一会话中为不同的数据库执行一次(或几次)该功能,这是有意义的。要在同一数据库中重复使用,请改为创建普通函数。

当然,您需要数据库的TEMPORARY privilege - 默认情况下用户拥有。

在参与其中时,我改进了几件事:

还有各种 变通办法

由于历史原因,BTW,customized options(“会话变量”)需要一个由两部分组成的名称(格式为 extension.variable )。事实证明,尽可能避免命名冲突是有用的。