我有一个脚本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_schema
是public
:脚本产生
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%
括在引号中或不在批处理文件中,则没有区别。
像往常一样,有两个问题:
set search path
陈述怎么会在我能够做到的时候“坚持不懈”
看它正在执行?更新:不相关,请忽略。:v1
参数传递给DO
脚本本身?环境:PostgreSQL 9.3.5 64位(Win);
奇怪:我确信这个脚本在两天前工作,唯一的变化就是删除geany插入的字节顺序标记(UTF BOMs make psql
gag)。
更新:前几天工作的原因是它正在考虑的架构的情况下运行。如果作为search_path
传递的架构名称不存在,则更改current_schema
(尝试从:v1
尝试并获取所需架构)将无法提供帮助 - 这使:v1
传递给DO
更为重要,因此可以更直接地使用它。
答案 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 - 默认情况下用户拥有。
在参与其中时,我改进了几件事:
转义标识符以防范 SQL注入和普通语法错误。使用quote_ident()
or format()
进行更复杂的操作。
您不需要将分号连接到单个SQL命令的末尾。
您可以一次EXECUTE
多个SQL语句。 (现在你需要在语句之间加一个分号。)
还有各种 变通办法 :
extension.variable
)。事实证明,尽可能避免命名冲突是有用的。