1

我有一个 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);

该脚本可以正常工作(并且在某些情况下仍然可以正常工作)。
将表的主键从 a 更改int为 a后,它停止工作uid。循环在执行过程中停止,不显示任何错误消息(调试设置为最佳级别)。

奇怪的是脚本确实可以工作,即使在更改之后,如果我只是将它粘贴到 psql shell 中而不是从代码中执行它。此外,如果我解压缩循环并仅编写循环执行内联的所有语句,它在从 java 代码执行时可以工作。

我已经花了几天的时间在这上面,我对如何继续一无所知。有任何想法吗 ?

4

1 回答 1

1

我看到几个问题:

  • 需要清理标识符,否则您可能会遇到异常,或者更糟的是,打开SQL 注入的攻击路径。除非双引号,否则标识符可以是非法字符串。有几种方法可以让 Postgres 自动处理这些问题。
    我使用了以下两种形式:

  • 您的功能正在删除所有约束,而您只想根据您的描述删除 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;
于 2013-08-14T18:04:57.807 回答