2

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

なぜこれが純粋なCREATEではなくSELECTステートメントに変換されたのですか?

4

2 に答える 2

4

あなたの設定はかなりねじれています。ビューの名前の一部をプレーン テキスト列に保存するのではなく、テーブルの複合型に保存するのはなぜですか?

とにかく、次のように機能します。

マッチング問題の設定:

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 インジェクションから保護する必要があります。識別子リテラルに関数を使用します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;
于 2012-10-10T01:16:34.287 に答える
2

代入式でのコンマの使い方を誤解しています。スカラーではなくquery配列 ( ) になります。RECORD連結を使用します。

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';
于 2012-10-09T20:29:43.413 に答える