psql
でスクリプトを実行してPostgresで新しいスキーマを作成するとき、それをテーブルスペースに配置したいと思います。このテーブルスペースが存在しない場合は、最初に作成します。デフォルトのSQLにはこのオプションがないため、関数を作成しました。
_CREATE OR REPLACE FUNCTION make_tablespace(tablespace CHARACTER,
directory CHARACTER,
owner CHARACTER)
RETURNS void
AS
$$
BEGIN
IF tablespace = '' THEN
RAISE EXCEPTION 'No tablespace.';
END IF;
PERFORM SPCNAME FROM PG_TABLESPACE WHERE SPCNAME=tablespace;
IF NOT FOUND THEN
IF directory = '' THEN
RAISE EXCEPTION 'No directory.';
END IF;
IF owner = '' THEN
RAISE EXCEPTION 'No owner.';
END IF;
EXECUTE 'CREATE TABLESPACE '||tablespace||' OWNER '||owner||' LOCATION '''||directory||''';';
RAISE NOTICE 'Tablespace % created.', tablespace;
ELSE
RAISE NOTICE 'Tablespace % already exists.', tablespace;
END IF;
END $$ LANGUAGE plpgsql;
_
残念ながら、それを実行すると(select make_tablespace('marco', '/opt/marco', 'marco');
)、エラーが発生します。
エラー:CREATE TABLESPACEは関数またはマルチコマンド文字列から実行できません
私はインターネットを検索しましたが、dblink
パッケージを使用することで回避策(数年前)があるようです。これをインストールしたくない。今日は別の方法がありますか? SQLステートメントを文字列として返すことができますが、それを実行するにはどうすればよいですか?
ここでのポイントは、Postgresの関数は、真のストアドプロシージャとほとんど同じではありません。 (ストアドプロシージャとは異なり)Postgres関数は、外部トランザクションのコンテキストで実行されます。したがって、VACUUM
、_CREATE DATABASE
_、...、_CREATE TABLESPACE
_など、トランザクションブロックで実行できないコマンドは実行できません。マニュアルはそれについて明確です:
_
CREATE TABLESPACE
_は、トランザクションブロック内では実行できません。
これらのコマンドは、単一のSQLコマンドとして実行する必要があります。現在、Postgres(バージョン9.6まで)にも自律トランザクションがありません。これは回避策となる可能性があります。
したがって、コマンドを関数またはトランザクションに含める唯一の回避策は、自律型トランザクションを dblink
で偽造することです、あなたがすでに見つけたように。
コードには他にもいくつかの小さな問題があります。私は提案します:
_CREATE OR REPLACE FUNCTION make_tablespace(tablespace text,
directory text,
owner text)
RETURNS void AS
$func$
BEGIN
IF tablespace <> '' THEN -- catches '' *and* NULL
-- do nothing
ELSE
RAISE EXCEPTION 'No tablespace.';
END IF;
IF EXISTS (SELECT 1 FROM pg_tablespace WHERE spcname = tablespace) THEN
RAISE NOTICE 'Tablespace % already exists.', tablespace;
EXIT;
END IF;
IF directory <> '' THEN
ELSE
RAISE EXCEPTION 'No directory.';
END IF;
IF owner <> '' THEN
ELSE
RAISE EXCEPTION 'No owner.';
END IF;
PERFORM dblink_connect('myserver'); -- name of foreign server
PERFORM dblink_exec(format('CREATE TABLESPACE %I OWNER %I LOCATION %L', tablespace, owner, directory));
RAISE NOTICE 'Tablespace % created.', tablespace;
PERFORM dblink_disconnect();
END
$func$ LANGUAGE plpgsql;
_
主なポイント:
character
を使用しないでください。 _text.
_ ''
_を確認しながら、NULL
も確認します。format()
を使用して、簡潔でクリーンな動的SQLコードを作成し、ユーザー入力を適切にエスケープして、卑劣な構文エラーやSQLインジェクションを回避します。コード例と詳細な説明を含む関連する回答: