我制作了一个小的pl / pgsql脚本来重命名一些序列(添加前缀)并将其架构设置为“public”。但是我不明白为什么但是我的'ELSE'指令只在循环中执行一次,这不符合逻辑,因为我有很多行,'nspname'的值不是'docuprocess':
CREATE OR REPLACE FUNCTION move_schemas_to_public(target_schemas text[]) RETURNS integer AS $procedure$
DECLARE
rec RECORD;
sql text;
newname text;
nbreq integer := 0;
tabsize integer := array_length(target_schemas, 1);
i integer := 1;
debug boolean := false;
BEGIN
-- [...]
FOR rec in
select nspname, c.relname
from pg_class c
inner join pg_namespace ns
on (c.relnamespace = ns.oid)
where c.relkind = 'S'
and ns.nspname = any(target_schemas)
order by 1, 2
LOOP
IF rec.nspname = 'docuprocess' THEN
newname := rec.relname;
ELSE
-- Why these instructions are executed only once : -----
newname := rec.nspname||'_'||rec.relname;
sql := 'ALTER SEQUENCE '||rec.nspname||'.'||rec.relname||' RENAME TO '||newname;
RAISE NOTICE '%', sql;
IF debug is not true THEN
EXECUTE sql;
END IF;
nbreq := nbreq + 1;
--------------------------------------------------------
END IF;
sql := 'ALTER SEQUENCE '||rec.nspname||'.'||newname||' SET SCHEMA public';
RAISE NOTICE '%', sql;
IF debug is not true THEN
EXECUTE sql;
END IF;
nbreq := nbreq + 1;
END LOOP;
-- [...]
RETURN nbreq;
END;
select move_schemas_to_public(
-- schemas list
ARRAY[
'docufacture',
'docuprocess',
'formulaire',
'notification'
]
);
以下是循环SQL查询的结果:
[nspname];[relname]
"docufacture";"exportdoc_idexportdoc_seq"
"docufacture";"tableau_idcolonne_seq"
"docuprocess";"dp_action_champsdocuged_seq"
"docuprocess";"dp_action_commentaire_seq"
"docuprocess";"dp_action_docuged_seq"
"docuprocess";"dp_action_email_id_seq"
"docuprocess";"dp_action_formulaire_seq"
"docuprocess";"dp_action_id_seq"
"docuprocess";"dp_action_imprimer_id_seq"
"docuprocess";"dp_action_lancer_processus_id_seq"
"docuprocess";"dp_action_lancer_programme_id_seq"
"docuprocess";"dp_action_seq"
"docuprocess";"dp_action_transfert_fichier_id_seq"
"docuprocess";"dp_deroulement_etape_seq"
"docuprocess";"dp_deroulement_processus_seq"
"docuprocess";"dp_etape_seq"
"docuprocess";"dp_indisponibilite_seq"
"docuprocess";"dp_intervenant_seq"
"docuprocess";"dp_processus_seq"
"docuprocess";"dp_type_action_seq"
"formulaire";"champ_id_seq"
"formulaire";"fond_id_seq"
"formulaire";"formulaire_id_seq"
"formulaire";"modele_id_seq"
"notification";"notification_id_seq"
提前感谢您的宝贵帮助。
答案 0 :(得分:1)
我终于找到了问题的根源!在我的函数的开头(蒙版部分" [...]"),我有一个循环,它重命名作为参数传递的模式中的表,并将这些表移动到模式' public&#39 ;。此时,表格所拥有的序列存在于'文件中。和'通知'模式会自动移动到公共模式中。
所以,我只需重命名这些模式的序列,而不是移动它们。但是,我并不真正理解为什么序列的'docuprocess'和'公式化'没有以同样的方式移动!
的确,如果我在表格移动后尝试执行以下请求......
ALTER SEQUENCE docufacture.exportdoc_idexportdoc_seq RENAME TO docufacture_exportdoc_idexportdoc_seq
......我收到了这个错误:
ERROR: relation "docufacture.exportdoc_idexportdoc_seq" does not exist
...因为" exportdoc_idexportdoc_seq"已经转移到公共架构。
如果我在表格移动后尝试执行以下请求......
ALTER SEQUENCE exportdoc_idexportdoc_seq SET SCHEMA public;
......我收到了这个错误:
ERROR: cannot move an owned sequence into another schema
如果有人对此有一些解释,我们将非常感激。 非常感谢!
编辑:
因此,一种解决方案是分3步进行:
以下是代码:
CREATE OR REPLACE FUNCTION move_schemas_to_public(target_schemas text[]) RETURNS integer AS $procedure$
DECLARE
rec RECORD;
sql text;
newname text;
nbreq integer := 0;
tabsize integer := array_length(target_schemas, 1);
i integer := 1;
debug boolean := false;
BEGIN
SET lc_messages TO 'en_US.UTF-8';
-- sequences renamming
FOR rec in
select ns.nspname, c.relname
from pg_class c
inner join pg_namespace ns
on (c.relnamespace = ns.oid)
where c.relkind = 'S'
and ns.nspname = any(target_schemas)
LOOP
IF rec.nspname != 'docuprocess' THEN
newname := quote_ident(rec.nspname||'_'||rec.relname);
sql := 'ALTER SEQUENCE '||quote_ident(rec.nspname)||'.'||quote_ident(rec.relname)||' RENAME TO '||newname;
RAISE NOTICE '%', sql;
IF debug is not true THEN
EXECUTE sql;
END IF;
nbreq := nbreq + 1;
END IF;
END LOOP;
-- END sequences
-- tables
FOR rec in
SELECT table_schema, table_name
from information_schema.tables
where table_type = 'BASE TABLE'
and table_schema = any(target_schemas)
LOOP
IF rec.table_schema = 'docuprocess' THEN
newname := rec.table_name;
ELSE
newname := rec.table_schema||'_'||rec.table_name;
sql := 'ALTER TABLE '||rec.table_schema||'.'||rec.table_name||' RENAME TO '||newname;
RAISE NOTICE '%', sql;
IF debug is not true THEN
EXECUTE sql;
END IF;
nbreq := nbreq + 1;
END IF;
sql := 'ALTER TABLE '||rec.table_schema||'.'||newname||' SET SCHEMA public';
RAISE NOTICE '%', sql;
IF debug is not true THEN
EXECUTE sql;
END IF;
nbreq := nbreq + 1;
END LOOP;
-- END tables
-- remaining sequences shifting
FOR rec in
select ns.nspname, c.relname
from pg_class c
inner join pg_namespace ns
on (c.relnamespace = ns.oid)
where c.relkind = 'S'
and ns.nspname = any(target_schemas)
LOOP
sql := 'ALTER SEQUENCE '||quote_ident(rec.nspname)||'.'||quote_ident(rec.relname)||' SET SCHEMA public';
RAISE NOTICE '%', sql;
IF debug is not true THEN
EXECUTE sql;
END IF;
nbreq := nbreq + 1;
END LOOP;
-- END sequences
-- [...] Move functions, drop empty schemas
RETURN nbreq;
END;
$procedure$
LANGUAGE plpgsql;
select move_schemas_to_public(
-- schemas list
ARRAY[
'docufacture',
'docuprocess',
'formulaire',
'notification'
]
);
要完成,我想特别感谢" Erwin Brandstetter"他的高级帮助和建议。
答案 1 :(得分:0)
RENAME TO
之后此行中缺少空格:
sql := 'ALTER SEQUENCE '||rec.nspname||'.'||rec.relname||' RENAME TO '||newname;
因此,在第一个不在模式docuprocess
中的序列执行sql
语句并引发一个中止循环的错误。
另请注意,您不必ORDER BY
rec
{{1}}查询,因为您正在评估循环中的记录属性而不使用合格记录的顺序。
答案 2 :(得分:0)
我注意到您没有在{1}} SQL语句列表中对nspname
进行表限定:
SELECT
select nspname, c.relname
from pg_class c
inner join pg_namespace ns
on (c.relnamespace = ns.oid)
where c.relkind = 'S'
and ns.nspname = any(target_schemas)
order by 1, 2
子句中的那个是表格限定的。
您没有提供函数标头,但如果存在同名WHERE
的变量或函数参数,则它优先。然后,您将在查询结果中获得该变量的常量值,这将解释观察到的行为。
首先允许这样的命名冲突是一个坏主意。我很高兴用nspname
预先添加变量和参数。像_
一样
但是如果你有这样的冲突,你需要在你的SQL语句中是明确的,并且总是表限定不明确的列名。或者,为简单起见,所有列名称。
_nspname
类似案例:
如果这不是问题,则可能是缺少特权的情况。 Per pg 8.4 documentation:
您必须拥有序列才能使用
select ns.nspname, c.relname from pg_class c ...
。改变一个 在序列的模式中,您还必须拥有新模式的ALTER SEQUENCE
特权。
应该有错误信息!检查数据库日志......
在动态SQL中使用时需要清理标识符:
CREATE
等。 - 在所有情况下。否则,如果您的任何标识符是非标准的(大小写混合,保留字,空格,......),则您的语句会中断。甚至允许SQL注入。 (!)
在构建新名称之前,请小心 来应用...
newname := quote_ident(rec.relname);
ELSE
...
newname := quote_ident(rec.nspname||'_'||rec.relname);
sql := 'ALTER SEQUENCE ' || quote_ident(rec.nspname) || '.' || quote_ident(rec.relname)
|| ' RENAME TO ' || newname;
。
format()
。更多细节在这里:
可能是时候开始考虑upgrade to a current version。
最后,您的标识符变得越来越长。请记住,典型的最大长度为63字节: