0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PostgreSQLにおいて、スキーマ作成時にバリデーションを走らせる

0
Last updated at Posted at 2022-06-26

業務で必要になったので実装した。

やりたいこと

  • PostgreSQLにおいて、あるデータベース内に作られるスキーマの数を5以下に制限したい。
    • (information_schema, pg_*,publicを除く)
  • 複数のクライアントから同時にスキーマが作成される可能性があるので、競合状態になるのを防ぎたい
    • [追記]本ページの実装では競合状態を防げていなかった。テーブルロックが必要だった。
  • (既存実装の都合上) SQLのクエリ内で全てを完結させたい

実装

スキーマの新規作成時に、以下のクエリを実行する。

CREATE OR REPLACE PROCEDURE validate_number_of_schemas() AS $$ 
DECLARE
  schema_cnt int;
BEGIN
  SELECT COUNT(nspname)  
  INTO schema_cnt                           -- schema_cnt に代入。
  FROM pg_catalog.pg_namespace;
  WHERE nspname NOT LIKE 'pg_%' AND nspname NOT IN ('information_schema', 'public')

  IF schema_cnt > 3 THEN
    RAISE EXCEPTION 'Schema cannot be created any more!';
  END IF;
END;
$$ LANGUAGE plpgsql; 

BEGIN;
CREATE SCHEMA new_schema;
CALL validate_number_of_schemas();
COMMIT;

ref. PL/pgSQLを使い、PostgreSQLのクエリ内で条件分岐して例外発生を起こす

実機検証

下準備

動作確認用のデータベースを作成する。

$ createdb test_db
$ psql -d test_d

スキーマを3つ(上限まで)作る。

test_db=# CREATE SCHEMA first_schema;
CREATE SCHEMA
test_db=# CREATE SCHEMA second_schema;
CREATE SCHEMA
test_db=# CREATE SCHEMA third_schema;
CREATE SCHEMA

スキーマが3つできていることを確認する。

test_db=# \dn
      List of schemas
     Name      |   Owner    
---------------+------------
 first_schema  | daiki-kudo
 public        | daiki-kudo
 second_schema | daiki-kudo
 third_schema  | daiki-kudo
(4 rows)
スキーマ一覧は pg_catalog.pg_namespace テーブルに収納されている
test_db=# SELECT * FROM pg_catalog.pg_namespace;
  oid   |      nspname       | nspowner |                          nspacl                           
--------+--------------------+----------+-----------------------------------------------------------
     99 | pg_toast           |       10 | 
     11 | pg_catalog         |       10 | {"\"daiki-kudo\"=UC/\"daiki-kudo\"","=U/\"daiki-kudo\""}
   2200 | public             |       10 | {"\"daiki-kudo\"=UC/\"daiki-kudo\"","=UC/\"daiki-kudo\""}
  13657 | information_schema |       10 | {"\"daiki-kudo\"=UC/\"daiki-kudo\"","=U/\"daiki-kudo\""}
 180900 | first_schema       |       10 | 
(5 rows)

従って以下でもスキーマ一覧が確認できる。

test_db=# SELECT COUNT(*) FROM pg_catalog.pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname NOT IN ('information_schema', 'public');
 count 
-------
     5
(1 row)

バリデーションが効くことの確認

以下を実行して、4つ目のスキーマの作成を試みる。
(---以下は実際に実行した時の出力)

CREATE OR REPLACE PROCEDURE validate_number_of_schemas() AS $$ 
DECLARE
  schema_cnt int;
BEGIN
  SELECT COUNT(nspname)  
  INTO schema_cnt                           -- schema_cnt に代入。
  FROM pg_catalog.pg_namespace;
  WHERE nspname NOT LIKE 'pg_%' AND nspname NOT IN ('information_schema', 'public')

  IF schema_cnt > 3 THEN
    RAISE EXCEPTION 'Schema cannot be created any more!';
  END IF;
END;
$$ LANGUAGE plpgsql; 
--- CREATE PROCEDURE

BEGIN;
CREATE SCHEMA fourth_schema;  ----------------- 🌟
--- CREATE SCHEMA
CALL validate_number_of_schemas();
--- ERROR:  Schema cannot be created any more!
--- CONTEXT:  PL/pgSQL function validate_number_of_schemas() line 7 at RAISE
COMMIT;
--- ROLLBACK

トランザクション中に 他のセッションにおいて、作成中のスキーマのdirty read が発生しないことの確認

前章の🌟の時点で別のシェルで $ psql -d test_dbを開き、fourth_schemaがスキーマのリストに含まれていない(つまり、dirty readが発生しない)ことを確認する。

test_db=# \dn
      List of schemas
     Name      |   Owner    
---------------+------------
 first_schema  | daiki-kudo
 public        | daiki-kudo
 second_schema | daiki-kudo
 third_schema  | daiki-kudo
(4 rows)

まとめ

"create schema" と "validation" を同一トランザクション内に入れてやる、というのが肝。

BEGIN;
CREATE SCHEMA fourth_schema;
CALL validate_number_of_schemas();
COMMIT;
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?