我有一个plpgsql脚本(编者注:它实际上是一个函数),它包含一个循环,它删除了由eclipse-link生成的一些表的主键约束。它看起来像这样:
CREATE OR REPLACE FUNCTION remove_tables_constraints()
RETURNS boolean AS
$BODY$
DECLARE
constraint_statment text;
BEGIN
FOR constraint_statment IN
SELECT 'ALTER TABLE '||nspname||'.'||relname||' DROP CONSTRAINT '||conname
FROM pg_constraint
INNER JOIN pg_class ON conrelid=pg_class.oid
INNER JOIN pg_namespace ON pg_namespace.oid=pg_class.relnamespace
where relname not in('exclude_table')
ORDER BY CASE WHEN contype='f' THEN 0 ELSE 1 END,contype,nspname,relname,conname LOOP
raise notice 'remove_tables_constraints run [%]', constraint_statment;
EXECUTE constraint_statment;
END LOOP;
RETURN true;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE COST 100;
select remove_tables_constraints();
使用以下命令执行脚本:
Statement st = connection.createStatement();
st.execute(scriptStringloadedFromFile);
脚本有效(在某些情况下仍然有效)
将表的主键从int
更改为uid
后,它停止了工作。循环在执行中期暂停,不显示任何错误消息(调试设置为最佳级别)。
奇怪的是,如果我只是将它粘贴到psql shell而不是从代码中执行,那么即使在更改之后脚本也能正常工作。而且,如果我解压缩循环并且只是编写循环执行内联的所有语句,它在从java代码执行它时起作用。
我已经花了几天时间,我对如何继续这一点毫无头绪。有什么想法吗?
答案 0 :(得分:1)
我看到了几个问题:
您need to sanitize identifiers或者您可以获得例外或更糟,请打开attack path for SQL injection。标识符可以是非法字符串,除非双引号。有几种方法让Postgres自动处理 我在下面使用了两种形式:
format()
%I
参数转换(Postgres 9.1 +)regclass
类型,which is even better for table names (IMO)。您的功能是删除所有约束,而您只想根据您的描述删除PK约束(contype = 'p'
)。
您不排除系统目录和其他系统架构。无论如何,这都应该失败。
不引用语言名称plpgsql
。这是一个标识符。
所有东西放在一起它看起来像这样:
CREATE OR REPLACE FUNCTION remove_tables_constraints()
RETURNS boolean AS
$func$
DECLARE
constraint_statment text;
BEGIN
FOR constraint_statment IN
SELECT format('ALTER TABLE %s DROP CONSTRAINT %I'
, c.oid::regclass, o.conname)
FROM pg_constraint o
JOIN pg_class c ON c.oid = o.conrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname <> 'exclude_table' -- just one? then <>
AND o.contype = 'p' -- only pk constraints
AND n.nspname NOT LIKE 'pg%' -- exclude system schemas!
AND n.nspname <> 'information_schema' -- exclude information schema!
ORDER BY n.nspname, c.relname, o.conname -- commented irrelevant item
LOOP
RAISE NOTICE 'remove_table_constraints run [%]', constraint_statment;
EXECUTE constraint_statment;
END LOOP;
RETURN TRUE;
END
$func$
LANGUAGE plpgsql;
或者更好,没有循环。在这里,我首先聚合成一个命令列表并执行一次:
CREATE OR REPLACE FUNCTION remove_tables_constraints()
RETURNS boolean AS
$func$
DECLARE
_sql text;
BEGIN
SELECT INTO _sql
string_agg(format('ALTER TABLE %s DROP CONSTRAINT %I'
, sub.tbl, sub.conname), E';\n')
FROM (
SELECT c.oid::regclass AS tbl, o.conname
FROM pg_constraint o
JOIN pg_class c ON c.oid = o.conrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname <> 'exclude_table' -- just one? then <>
AND o.contype = 'p' -- only pk constraints
AND n.nspname NOT LIKE 'pg%' -- exclude system schemas!
AND n.nspname <> 'information_schema' -- exclude information schema!
ORDER BY n.nspname, c.relname, o.conname -- commented irrelevant item
LIMIT 10
) sub;
RAISE NOTICE E'remove_table_constraints:\n%', _sql;
EXECUTE _sql;
RETURN TRUE;
END
$func$
LANGUAGE plpgsql;