如何使用 format() 函数引用限定表名?

tin*_*lyx 5 postgresql dynamic-sql plpgsql postgresql-10

format我在使用下面示例中的函数正确引用表名时遇到一些问题。

CREATE OR REPLACE FUNCTION copy_table(_source_tbl regclass, _target_tbl text)
 RETURNS bool AS $func$
DECLARE query_str text; 
BEGIN
  query_str = format($fmt$ DROP TABLE IF EXISTS %1$I; CREATE TABLE %1$I AS (TABLE %s); $fmt$, _target_tbl, _source_tbl);
  EXECUTE query_str;
  RAISE NOTICE '%', query_str;
 RETURN True;
END $func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

我的困境是我想引用输入表名称_target_tbl作为标识符(以避免 SQL 注入)。但是,给定完整的表名称ex.test1,这会导致架构部分ex.被视为表名称的一部分,并public."ex.test1"在默认public.架构中创建表,如下所示。

我应该如何在此处正确引用/格式化标识符?

=> SELECT copy_table('ex.test', 'ex.test1');
NOTICE:  table "ex.test1" does not exist, skipping
NOTICE:   DROP TABLE IF EXISTS "ex.test1"; CREATE TABLE "ex.test1" AS (TABLE ex.test);

=> \dt ex.test1
Did not find any relation named "ex.test1".
=> \dt "ex.test1"
         List of relations
 Schema |   Name   | Type  | Owner 
--------+----------+-------+-------
 public | ex.test1 | table | 
(1 row)
Run Code Online (Sandbox Code Playgroud)

这是 PostgreSQL 10.3 的情况。

Erw*_*ter 3

为避免目标表名称含糊不清,请分别提供架构名称和表名称。对于现有表,Postgres 可以使用当前的search_path默认模式作为非限定表名的具有该名称的对象的第一个模式。由于这对于(尚)不存在的表显然是不可能的,因此我们必须更加明确。

如果您通常希望将目标表放置在与源相同的架构中,为了方便起见,您仍然可以省略架构名称并使函数默认为(现有!)源表的架构。喜欢:

CREATE OR REPLACE FUNCTION copy_table(_source_tbl regclass
                                    , _target_tbl text
                                    , _target_schema text = NULL)
  RETURNS bool AS
$func$
DECLARE
   query_str text; 
BEGIN
   IF _target_schema IS NULL THEN               -- if no target schema provided ...
      SELECT c.relnamespace::regnamespace::text -- ... default to schema of input table
      FROM   pg_class c
      WHERE  c.oid = _source_tbl
      INTO  _target_schema;
   END IF;

   query_str = format('DROP TABLE IF EXISTS %1$I.%2$I;
                       CREATE TABLE %1$I.%2$I AS (TABLE %3$s);'
    , _target_schema
    , _target_tbl
    , _source_tbl);

   EXECUTE query_str;
   RAISE NOTICE '%', query_str;
   RETURN true;
END
$func$  LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

称呼:

SELECT copy_table('ex.test', 'text1', 'ex');
Run Code Online (Sandbox Code Playgroud)

由于第三个(最后一个)参数_target_schema有默认值,我们可以省略它,在这种情况下,函数默认为源表的架构:

SELECT copy_table('ex.test', 'test1');
Run Code Online (Sandbox Code Playgroud)

即使我们没有明确提供源模式,也依赖于当前的search_path

SELECT copy_table('test', 'test1');
Run Code Online (Sandbox Code Playgroud)

有关的: