我有以下脚本来动态创建 PostgreSQL 数据库的 View 。
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?
最佳答案
您的设置非常扭曲。为什么要将 View 名称的一部分保存在表格的复合类型中,而不是将其保存在纯文本列中?
无论如何,它可以像这样工作:
设置匹配题:
CREATE SCHEMA x; -- demo in test schema
SET search_path = x;
CREATE TYPE mviews AS (id int, name text); -- composite type used in table
CREATE TABLE "Canadian_Parks" (name mviews);
INSERT INTO "Canadian_Parks"(name) VALUES
('(1,"canadian")')
,('(2,"islandic")'); -- composite types, seriously?
SELECT name, (name).* from "Canadian_Parks";
CREATE OR REPLACE FUNCTION cs_refresh_mviews()
RETURNS int LANGUAGE plpgsql SET search_path = x AS -- search_path for test
$func$
DECLARE
_parkname text;
BEGIN
FOR _parkname IN SELECT (name).name FROM "Canadian_Parks" LOOP
EXECUTE format('
CREATE OR REPLACE VIEW %1$I AS
SELECT * FROM "Canadian_Parks" WHERE (name).name = %2$L;
ALTER TABLE %1$I OWNER TO postgres'
, _parkname || '_view', _parkname);
END LOOP;
RETURN 1;
END
$func$;
SELECT cs_refresh_mviews();
DROP SCHEMA x CASCADE; -- clean up
要点
当您使用 execute 执行文本时,您需要防止SQL 注入(inject)。我用
format()
标识符 和文字 的函数
我使用语法
SELECT (name).name
来应对您奇怪的设置并立即提取我们需要的name
。同样,VIEW 需要读取
WHERE (name).name = ..
才能在此设置中工作。我删除了很多与问题无关的噪音。
RETURN 1
函数也可能毫无意义。只需使用RETURNS 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;
关于postgresql - PL/pgSQL 在 EXECUTE 中创建或替换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12807235/