Postgresql PL/pgSQL在EXECUTE中创建或替换

Postgresql PL/pgSQL在EXECUTE中创建或替换,postgresql,plpgsql,dynamic-sql,materialized-views,Postgresql,Plpgsql,Dynamic Sql,Materialized Views,我有以下脚本来动态地将视图创建到PostgreSQL数据库中 CREATE OR REPLACE FUNCTION cs_refresh_mviews() RETURNS integer AS $$ DECLARE mviews RECORD; query text; park_name text; ppstatements int; BEGIN RAISE NOTICE 'Creating views...'; FOR mviews IN SE

我有以下脚本来动态地将视图创建到PostgreSQL数据库中

CREATE OR REPLACE FUNCTION cs_refresh_mviews() RETURNS integer AS $$
DECLARE
    mviews RECORD;
    query text;
    park_name text;
    ppstatements int;
BEGIN
    RAISE NOTICE 'Creating views...';

    FOR mviews IN SELECT name FROM "Canadian_Parks" LOOP
        park_name := mviews.name;
        RAISE NOTICE 'Creating or replace view %s...', mviews.name; 
        query := 'CREATE OR REPLACE VIEW %_view AS
          SELECT * from "Canadian_Parks" where name=''%'';
          ALTER TABLE %_view OWNER TO postgres', park_name, park_name, park_name;
      --  RAISE NOTICE query;
        EXECUTE query;
    END LOOP;

    RAISE NOTICE 'Done refreshing materialized views.';
    RETURN 1;
END;
$$ LANGUAGE plpgsql;
我已确认字符串的完整性,例如

CREATE OR REPLACE VIEW Saguenay_St__Lawrence_view AS
SELECT * from "Canadian_Parks" where name='Saguenay_St__Lawrence';
ALTER TABLE Saguenay_St__Lawrence_view OWNER TO postgres
通过手动将其提交到数据库并获得成功响应来分配给查询变量

但是,如果我尝试使用

SELECT cs_refresh_mviews();
将显示以下错误:

ERROR:  query "SELECT 'CREATE OR REPLACE VIEW %_view AS SELECT * from "Canadian_Parks" where name=''%''; ALTER TABLE %_view OWNER TO postgres', park_name, park_name, park_name" returned 4 columns
CONTEXT:  PL/pgSQL function "cs_refresh_mviews" line 32 at assignment

********** Error **********

ERROR: query "SELECT 'CREATE OR REPLACE VIEW %_view AS SELECT * from "Canadian_Parks" where name=''%''; ALTER TABLE %_view OWNER TO postgres', park_name, park_name, park_name" returned 4 columns
SQL state: 42601
Context: PL/pgSQL function "cs_refresh_mviews" line 32 at assignment

为什么要将其转换为SELECT语句,而不是纯CREATE语句?

您误解了逗号在赋值表达式中的用法。 它将
查询
转换为数组(
记录
),而不是标量。 使用串联:

park_name := quote_ident(mviews.name||'_view');
query := 'CREATE OR REPLACE VIEW '||park_name||' AS SELECT * from "Canadian_Parks" where name='||quote_literal(mviews.name)||'; ALTER TABLE '||park_name||' OWNER TO postgres';

你的设置很扭曲。为什么要将视图名称的一部分保存在表的复合类型中,而不是保存在纯文本列中

无论如何,它可以这样工作:

设置匹配问题: 要点
  • 当您使用execute执行文本时,需要防止SQL注入。我将函数用于标识符和文本

  • 我使用语法
    SELECT(name).name
    来处理您奇怪的设置,并立即提取我们需要的
    name

  • 类似地,视图需要读取
    WHERE(name).name=..
    才能在此设置中工作

  • 我消除了许多与问题无关的噪音

  • 让函数
    返回1
    也可能没有意义。只需使用
    返回void
    定义函数即可。不过,我保留了它,以便与问题相匹配

解开装置 可能的情况是:

CREATE SCHEMA x;
SET search_path = x;

CREATE TABLE canadian_parks (id serial primary key, name text);
INSERT INTO canadian_parks(name) VALUES ('canadian'), ('islandic');

SELECT * from canadian_parks;

CREATE OR REPLACE FUNCTION cs_refresh_mviews()
  RETURNS void LANGUAGE plpgsql SET search_path = x AS
$func$
DECLARE
    parkname text;
BEGIN

FOR parkname IN SELECT name FROM canadian_parks LOOP
   EXECUTE format('
      CREATE OR REPLACE VIEW %1$I AS
      SELECT * FROM canadian_parks WHERE name = %2$L;
      ALTER TABLE %1$I OWNER TO postgres'
      , parkname || '_view', parkname);
END LOOP;

END
$func$;

SELECT cs_refresh_mviews();

DROP SCHEMA x CASCADE;
CREATE SCHEMA x;
SET search_path = x;

CREATE TABLE canadian_parks (id serial primary key, name text);
INSERT INTO canadian_parks(name) VALUES ('canadian'), ('islandic');

SELECT * from canadian_parks;

CREATE OR REPLACE FUNCTION cs_refresh_mviews()
  RETURNS void LANGUAGE plpgsql SET search_path = x AS
$func$
DECLARE
    parkname text;
BEGIN

FOR parkname IN SELECT name FROM canadian_parks LOOP
   EXECUTE format('
      CREATE OR REPLACE VIEW %1$I AS
      SELECT * FROM canadian_parks WHERE name = %2$L;
      ALTER TABLE %1$I OWNER TO postgres'
      , parkname || '_view', parkname);
END LOOP;

END
$func$;

SELECT cs_refresh_mviews();

DROP SCHEMA x CASCADE;