ビューとルールシステム

Postgres におけるビューの実装

Postgres におけるビューはルールシステム を利用して実装されています。実際において、

    CREATE VIEW myview AS SELECT * FROM mytab;
と、二つのコマンド
    CREATE TABLE myview (same attribute list as for mytab);
    CREATE RULE "_RETmyview" AS ON SELECT TO myview DO INSTEAD
        SELECT * FROM mytab;
の間には全く差異がありません。 というのは、CREATE VIEW コマンドによって内部的に全く同じ 処理が行われるからです。しかし副作用もあります。 その一つは、Postgres システムカタログ のビューについての情報はテーブルの情報と同一ですので、パーサ にとってもテーブルとビューは同じものになります。リレーションに ついても同じです。現時点の重要課題です。

SELECT ルールの動き

たとえコマンドが INSERT、 UPDATE または DELETE であっても、 ON SELECT ルールはすべての問合せに対し最後に適用され ます。 そして、それぞれは他と異なるせマンテックを持っていて パースツリーを新規に生成せずに、そこにあるものを修正します。 したがってSELECT ルールが一番初めに評価されなければなりません。

現時点では、たった一つのアクションだけが許されていて、 それは INSTEAD である SELECT アクションです。 この制約は一般のユーザが何をしてもルールシステムが堅牢で ある必要性があって、実のビューのルールに対する ON SELECT のルールを規制します。

このドキュメントの例としてあげているのは、ちょっとした演算をする 二つのジョインのビューと、翻ってこれらの機能を利用するいくつかの ビューを取り上げます。 最終結果がなんらかのマジック機能によりあたかも実テーブルのよう に振舞うビューになるように、始めの二つのビューの内の一つが INSERT、UPDATE および DELETE 操作に対するルールを後で追加する ことでカスタマイズされます。 始めて学ぶための例としては決して簡単ではなく先に進むことを躊躇 させるかもしれませんが、多くの別々の例を持ち出して頭の混乱を招 くよりも、すべての論点をステップ毎に追ってゆく一つの例をあげる ほうが良いと思われます。

例として操作するデータベースの名前は al_bundy です。 なぜこのような名前なのかはすぐ解ります。 下位の二つの整数値を返すちょっとした min() 関数を必要としますので 手続き言語 PL/pgSQL がインストールされている必要があります。 関数の生成は以下のようにします。

    CREATE FUNCTION min(integer, integer) RETURNS integer AS
        'BEGIN
            IF $1 < $2 THEN
                RETURN $1;
            END IF;
            RETURN $2;
        END;'
    LANGUAGE 'plpgsql'; 

初めの二つのルールシステムの評価に必要な実際のテーブルは 以下のものです。

    CREATE TABLE shoe_data (
        shoename   char(10),      -- 主キー
        sh_avail   integer,       -- 在庫組数
        slcolor    char(10),      -- 好まれる靴紐の色
        slminlen   float,         -- 最短の靴紐の長さ
        slmaxlen   float,         -- 最長の靴紐の長さ
        slunit     char(8)        -- 長さの単位
    );

    CREATE TABLE shoelace_data (
        sl_name    char(10),      -- 主キー
        sl_avail   integer,       -- 在庫組数
        sl_color   char(10),      -- 靴紐の色
        sl_len     float,         -- 靴紐の長さ
        sl_unit    char(8)        -- 長さの単位
    );

    CREATE TABLE unit (
        un_name    char(8),       -- 主キー
        un_fact    float          -- cm 変換の要素
    );
読者のほとんどが靴を履くでしょうからこのデータは確かに 使えますよね。もちろん世の中には靴紐のいらない靴 もありますが、それではアルの生活が成り立ちませんので 無視することにします。

ビューは次のようにして作られます。

    CREATE VIEW shoe AS
        SELECT sh.shoename,
               sh.sh_avail,
               sh.slcolor,
               sh.slminlen,
               sh.slminlen * un.un_fact AS slminlen_cm,
               sh.slmaxlen,
               sh.slmaxlen * un.un_fact AS slmaxlen_cm,
               sh.slunit
          FROM shoe_data sh, unit un
         WHERE sh.slunit = un.un_name;

    CREATE VIEW shoelace AS
        SELECT s.sl_name,
               s.sl_avail,
               s.sl_color,
               s.sl_len,
               s.sl_unit,
               s.sl_len * u.un_fact AS sl_len_cm
          FROM shoelace_data s, unit u
         WHERE s.sl_unit = u.un_name;

    CREATE VIEW shoe_ready AS
        SELECT rsh.shoename,
               rsh.sh_avail,
               rsl.sl_name,
               rsl.sl_avail,
               min(rsh.sh_avail, rsl.sl_avail) AS total_avail
          FROM shoe rsh, shoelace rsl
         WHERE rsl.sl_color = rsh.slcolor
           AND rsl.sl_len_cm >= rsh.slminlen_cm
           AND rsl.sl_len_cm <= rsh.slmaxlen_cm;
(今ある一番簡単な) shoelace ビュー用の CREATE VIEW コマンドは、リレーション shoelace と問合せ 範囲テーブルの中でリレーション shoelace が参照される時はいつでも、 適用されるべき書き換えルールの存在を示す項目を pg_rewrite に作ります。 ルールはルール条件を持たない INSTEAD です。 (SELECT rule は現在規定されていないので、非 SELECT ルール のところで取り上げられます。) ルールの条件は、問合せの条件とは異なることに注意して下さい! ルールアクションは条件を持っています。

ルールアクションはビュー生成コマンドの SELECT 文とまったく同じ 問合せツリーです。

注意書き: pg_rewrite 項目の (歴史的な理由により、出力用の問合せツリーではされは *NEW* および *CURRENT* という名前がついている) NEW および OLD に   たいする二つの特別な範囲テーブル項目は SELECT ルールには関係   ないことが解ります。

ではここで unitshoe_data および shoelace_data にデータをいれます。 アルはここで初めての SELECT を入力します。
    al_bundy=> INSERT INTO unit VALUES ('cm', 1.0);
    al_bundy=> INSERT INTO unit VALUES ('m', 100.0);
    al_bundy=> INSERT INTO unit VALUES ('inch', 2.54);
    al_bundy=> 
    al_bundy=> INSERT INTO shoe_data VALUES 
    al_bundy->     ('sh1', 2, 'black', 70.0, 90.0, 'cm');
    al_bundy=> INSERT INTO shoe_data VALUES 
    al_bundy->     ('sh2', 0, 'black', 30.0, 40.0, 'inch');
    al_bundy=> INSERT INTO shoe_data VALUES 
    al_bundy->     ('sh3', 4, 'brown', 50.0, 65.0, 'cm');
    al_bundy=> INSERT INTO shoe_data VALUES 
    al_bundy->     ('sh4', 3, 'brown', 40.0, 50.0, 'inch');
    al_bundy=> 
    al_bundy=> INSERT INTO shoelace_data VALUES 
    al_bundy->     ('sl1', 5, 'black', 80.0, 'cm');
    al_bundy=> INSERT INTO shoelace_data VALUES 
    al_bundy->     ('sl2', 6, 'black', 100.0, 'cm');
    al_bundy=> INSERT INTO shoelace_data VALUES 
    al_bundy->     ('sl3', 0, 'black', 35.0 , 'inch');
    al_bundy=> INSERT INTO shoelace_data VALUES 
    al_bundy->     ('sl4', 8, 'black', 40.0 , 'inch');
    al_bundy=> INSERT INTO shoelace_data VALUES 
    al_bundy->     ('sl5', 4, 'brown', 1.0 , 'm');
    al_bundy=> INSERT INTO shoelace_data VALUES 
    al_bundy->     ('sl6', 0, 'brown', 0.9 , 'm');
    al_bundy=> INSERT INTO shoelace_data VALUES 
    al_bundy->     ('sl7', 7, 'brown', 60 , 'cm');
    al_bundy=> INSERT INTO shoelace_data VALUES 
    al_bundy->     ('sl8', 1, 'brown', 40 , 'inch');
    al_bundy=> 
    al_bundy=> SELECT * FROM shoelace;
    sl_name   |sl_avail|sl_color  |sl_len|sl_unit |sl_len_cm
    ----------+--------+----------+------+--------+---------
    sl1       |       5|black     |    80|cm      |       80
    sl2       |       6|black     |   100|cm      |      100
    sl7       |       7|brown     |    60|cm      |       60
    sl3       |       0|black     |    35|inch    |     88.9
    sl4       |       8|black     |    40|inch    |    101.6
    sl8       |       1|brown     |    40|inch    |    101.6
    sl5       |       4|brown     |     1|m       |      100
    sl6       |       0|brown     |   0.9|m       |       90
    (8 rows)
アルが行うビューにたいする最も簡単な SELECT ですので、これで ビューの基本ルールを説明します。 'SELECT * FROM shoelace' はパーサによって処理され、次の パースツリーが生成されます。
    SELECT shoelace.sl_name, shoelace.sl_avail,
           shoelace.sl_color, shoelace.sl_len,
           shoelace.sl_unit, shoelace.sl_len_cm
      FROM shoelace shoelace;
この操作はルールシステムに伝えられます。ルールシステムは 範囲テーブルを参照し、なんらかのリレーションにたいしてルールが pg_rewrite に存在するか調べます。 (現時点ではたった一つの) shoelace にたいする 範囲テーブル項目を処理するときにパースツリーでルール '_RETshoelace' を見つけ出します。
    SELECT s.sl_name, s.sl_avail,
           s.sl_color, s.sl_len, s.sl_unit,
           float8mul(s.sl_len, u.un_fact) AS sl_len_cm
      FROM shoelace *OLD*, shoelace *NEW*,
           shoelace_data s, unit u
     WHERE bpchareq(s.sl_unit, u.un_name);
パーサが、演算と条件を適切な関数の呼び出しに変換した事に 注意して下さい。 しかし実際は、これは何も変更しません。 書き換えの最初のステップは二つの範囲テーブルをマージすることです。 これによってパースツリーは次のようになります。
    SELECT shoelace.sl_name, shoelace.sl_avail,
           shoelace.sl_color, shoelace.sl_len,
           shoelace.sl_unit, shoelace.sl_len_cm
      FROM shoelace shoelace, shoelace *OLD*,
           shoelace *NEW*, shoelace_data s,
           unit u;
ステップ 2 で、ルールアクションから条件をパースツリーに 付け加え、以下の結果をもたらします。
    SELECT shoelace.sl_name, shoelace.sl_avail,
           shoelace.sl_color, shoelace.sl_len,
           shoelace.sl_unit, shoelace.sl_len_cm
      FROM shoelace shoelace, shoelace *OLD*,
           shoelace *NEW*, shoelace_data s,
           unit u
     WHERE bpchareq(s.sl_unit, u.un_name);
次に、ステップ 3 では範囲テーブルの項目 (現在処理されている shoelace に対するものの一つ)を参照する パースツリーのすべての変数をルールアクションからの対応する目的 リストの式で置換します。そして、最終の問合せとなります。
    SELECT s.sl_name, s.sl_avail, 
           s.sl_color, s.sl_len, 
           s.sl_unit, float8mul(s.sl_len, u.un_fact) AS sl_len_cm
      FROM shoelace shoelace, shoelace *OLD*,
           shoelace *NEW*, shoelace_data s,
           unit u
     WHERE bpchareq(s.sl_unit, u.un_name);
翻って、人間の手で入力する場合の実際の SQL 文 は次の通りです。
    SELECT s.sl_name, s.sl_avail,
           s.sl_color, s.sl_len,
           s.sl_unit, s.sl_len * u.un_fact AS sl_len_cm
      FROM shoelace_data s, unit u
     WHERE s.sl_unit = u.un_name;
これは適用される最初のルールです。この間に範囲テーブルは成長 します。ですから、ルールシステムは範囲テーブルの項目をチェック し続けます。次は第二番 (shoelace *OLD*) です。 リレーション shoelace はルールを持っていますが この範囲テーブル項目はパースツリーのいかなる変数によっても参照 されないので無視されます。 残りの全ての範囲テーブル項目はpg_rewrite にルールが無いか参照されないかのいずれかですので、範囲テーブルの 終りに行きつきます。 書き換えは完了し、上記がオプティマイザに渡される最終結果となります。 オプティマイザは、パースツリーの変数で参照されない特別の 範囲テーブル項目を無視し、ビューの選択によってではなく、アルが入力 した上記の SELECT と全く同じのプランがプランナ/オプティマイザ によって生成されます。

ここで、ブルース兄弟が靴を買いにお店にやって来て、兄弟ですから、 同じ靴を履きたい、当然靴紐も同じものを、と言って来たときアルは 問題を抱えることになります。

店に置いてある靴紐の(色とサイズ)に一致する靴がどれで、 完全に一致するものの在庫が 2 組以上あるかどうかをアルは 知る必要に迫られます。 私達はアルにデータベースに問い合わせることを教えます。

    al_bundy=> SELECT * FROM shoe_ready WHERE total_avail >= 2;
    shoename  |sh_avail|sl_name   |sl_avail|total_avail
    ----------+--------+----------+--------+-----------
    sh1       |       2|sl1       |       5|          2
    sh3       |       4|sl7       |       7|          4
    (2 rows)
靴についてアルは専門家ですので、sh1 型の靴が該当することが 判ります。(sl7 の靴紐は茶色で、それに合う茶色の靴はブルース 兄弟の好みではありません。)

今回のパーサの出力は以下のパースツリーです。

    SELECT shoe_ready.shoename, shoe_ready.sh_avail,
           shoe_ready.sl_name, shoe_ready.sl_avail,
           shoe_ready.total_avail
      FROM shoe_ready shoe_ready
     WHERE int4ge(shoe_ready.total_avail, 2);
一番始めに適用されるルールは shoe_ready リレーション用のものでパースツリーにおける結果は以下のように なります。
    SELECT rsh.shoename, rsh.sh_avail,
           rsl.sl_name, rsl.sl_avail,
           min(rsh.sh_avail, rsl.sl_avail) AS total_avail
      FROM shoe_ready shoe_ready, shoe_ready *OLD*,
           shoe_ready *NEW*, shoe rsh,
           shoelace rsl
     WHERE int4ge(min(rsh.sh_avail, rsl.sl_avail), 2)
       AND (bpchareq(rsl.sl_color, rsh.slcolor)
            AND float8ge(rsl.sl_len_cm, rsh.slminlen_cm)
            AND float8le(rsl.sl_len_cm, rsh.slmaxlen_cm)
           );
実際、条件の中の AND 句は左右に式を有する AND 型の演算子ノードです。 しかし、前とは違ってより解りにくい結果となり、適用するルールもより 多くなります。 ですから、筆者はこれらを論理単位にグループ化するために付け加え られた場所の順序にしたがって括弧で括り、参照される次の範囲テーブル 項目であってルールを持っている リレーション shoe に対するルールに進むことにします。 適用の結果は以下の通りです。
    SELECT sh.shoename, sh.sh_avail,
           rsl.sl_name, rsl.sl_avail,
           min(sh.sh_avail, rsl.sl_avail) AS total_avail,
      FROM shoe_ready shoe_ready, shoe_ready *OLD*,
           shoe_ready *NEW*, shoe rsh,
           shoelace rsl, shoe *OLD*,
           shoe *NEW*, shoe_data sh,
           unit un
     WHERE (int4ge(min(sh.sh_avail, rsl.sl_avail), 2)
            AND (bpchareq(rsl.sl_color, sh.slcolor)
                 AND float8ge(rsl.sl_len_cm, 
                              float8mul(sh.slminlen, un.un_fact))
                 AND float8le(rsl.sl_len_cm, 
                              float8mul(sh.slmaxlen, un.un_fact))
                )
           )
       AND bpchareq(sh.slunit, un.un_name);
最終的に既に良く知られている shoelace に対する ルールを適用し(ここでは、多少複雑になったパースツリー上のもの)そして 結果を得ます。
    SELECT sh.shoename, sh.sh_avail,
           s.sl_name, s.sl_avail,
           min(sh.sh_avail, s.sl_avail) AS total_avail
      FROM shoe_ready shoe_ready, shoe_ready *OLD*,
           shoe_ready *NEW*, shoe rsh,
           shoelace rsl, shoe *OLD*,
           shoe *NEW*, shoe_data sh,
           unit un, shoelace *OLD*,
           shoelace *NEW*, shoelace_data s,
           unit u
     WHERE (    (int4ge(min(sh.sh_avail, s.sl_avail), 2)
                 AND (bpchareq(s.sl_color, sh.slcolor)
                      AND float8ge(float8mul(s.sl_len, u.un_fact), 
                                   float8mul(sh.slminlen, un.un_fact))
                      AND float8le(float8mul(s.sl_len, u.un_fact), 
                                   float8mul(sh.slmaxlen, un.un_fact))
                     )
                )
            AND bpchareq(sh.slunit, un.un_name)
           )
       AND bpchareq(s.sl_unit, u.un_name);
もう一度、これをルールシステムの最終結果と同一の結果をもたらす 実際の単純な形の SQL 文にしてみます。
    SELECT sh.shoename, sh.sh_avail,
           s.sl_name, s.sl_avail,
           min(sh.sh_avail, s.sl_avail) AS total_avail
      FROM shoe_data sh, shoelace_data s, unit u, unit un
     WHERE min(sh.sh_avail, s.sl_avail) >= 2
       AND s.sl_color = sh.slcolor
       AND s.sl_len * u.un_fact >= sh.slminlen * un.un_fact
       AND s.sl_len * u.un_fact <= sh.slmaxlen * un.un_fact
       AND sh.sl_unit = un.un_name
       AND s.sl_unit = u.un_name;
ルールの再帰的処理はビューからの一つの SELECT をパースツリー に書き換えます。これはビューが全く無かったときアルが入力 したものと全く同等です。

注意書き: ルールシステムにおいて(他のルールは別ですが)ビュールールの 再帰処理を中止する機構はありません。 この無限ループ(メモリの限界に到達してバックエンドが破壊される) を引き起こすにはには、テーブルを作成しCREATE RULE により 手作業でビュールールを他からの選択が、その他からのを選択する ように設定することによりのみ可能ですから、さほど深刻な問題 ではありません。 初めの CREATE VIEW では、二番目のリレーションは存在せず 第一のビューを第二のビューが選択することは有りえませんから このような状況は CREATE VIEW で起こり得ません。

非 SELECT 文のビュールール

これまでのビュールールの説明ではパースツリーの詳細二点 について触れませんでした。それらは、コマンドタイプおよび 結果リレーションです。実際、ビューのルールはこれらの情報を 必要としません。

SELECT と他のコマンドの対するパースツリーの間には大きな差異が有りません。 明らかにそれらは別のコマンドタイプを持っていて、今回は結果リレーション は結果がどこに行くのかを示す範囲テーブルの項目を指し示します。 このこと以外は全く同一です。 ですから、a と b の属性を持つテーブル t1 および t2 に対しての 二つの命令文のパースツリー、

    SELECT t2.b FROM t1, t2 WHERE t1.a = t2.a;

    UPDATE t1 SET b = t2.b WHERE t1.a = t2.a;
はほとんど同等です。

結果、両方のパースツリーが似たような実行プランになります。 それらは共に二つのテーブルにまたがったジョインです。UPDATE に対してt1 から抜けているカラムはオプティマイザが目的リスト に追加し、最終のパースツリーは、
    UPDATE t1 SET a = t1.a, b = t2.b WHERE t1.a = t2.a;
のようになって、ジョインを実行したエクゼキュータは、
    SELECT t1.a, t2.b FROM t1, t2 WHERE t1.a = t2.a;
と全く同じ結果のセットを作成します。とは言っても、UPDATE には ちょっとした問題があります。エクゼキュータは、ジョインが行う処理 の結果が何を意味しているかに関与しません。 エクゼキュータは単に結果となる行のセットを作成するだけです。 一つは SELECT コマンド でもう一つは UPDATE コマンドですが、相違はエクゼキュータを呼び出 す側で扱われることです。呼び出し側は( パースツリーを見て)、これが UPDATE であると解っていて、この結果がテーブル t1 に入らなければ ならないことを知っています。しかし、666 ある行の内、どの行が新し い行によって置換されなければならないのでしょうか。 実行されたプランは条件による 0 から 666 の間の 順序 の決まらないどんな数の行でも潜在的に生成し得るジョインです。

この問題を解決するため、UPDATE と DELETE 文の目的リストに別の 項目が付け加えられます。それは現在のタプル ID (ctid) です。 これは特別の機能をもったシステムの属性です。それは、その行に対 する当該ブロックの中のブロックと位置を持っています。テーブルが判っ ている場合、一つのデータブロックをフェッチすることによって数百万 の行を含む 1.5GB 容量のテーブルの中の一つの特定の行を 見つけ出すために ctid が利用出来ます。目的リストに ctid を付け加えた後、最終の結果のセットは以下のように定義されます。

    SELECT t1.a, t2.b, t1.ctid FROM t1, t2 WHERE t1.a = t2.a;
では Postgres の別の詳細説明に入ります。 この時点で、テーブルの行は上書きされていませんので ABORT TRANSACTION 処理は速いのです。UPDATE では(ctid を取り除いたあと)テーブルに 新結果の行が挿入され、その行のタプルヘッダ内では cmax を指し示す ctid と xmax の項目が現在のコマンドカウンタと現在のトランザクション ID にセットされます。 このようにして、旧行は隠されトランザクションがコミットされた時点 で掃除機が実際に削除します。

これらの詳細が全部解れば、どんなコマンドにたいしても全く同じように してビューのルールを単純に適用することが出来ます。そこに差異は存在 しません。

Postgres における View の能力

ここまでで、ビューの諸定義がオリジナルのパースツリーにどのように関与 するかを解説しました。第二の例では、簡単な SELECT from one view で四つのテーブルのジョインである最終パースツリーが作られます。 (事例は違った名前で二度使われます。)

利点

ビューをルールシステムとともに実装する利点は、 オプティマイザが、どのテーブルをスキャンすべきか、および またそれらのテーブル間の関連性、および ビューからの制約条件、およびたった一つのパースツリー内にある オリジナルの問合わせ条件についての情報を持っていることです。 オリジナルの問合せが既にビューに対するジョインである時の状況 でもあります。 オプティマイザはここでどれが問合せ処理の最適経路かを決定 しなければなりません。情報が多ければ多いほど、オプティマイザ はより良い決定を下すことが出来ます。そして Postgres に実装されているルール システムではこれが現時点で、問合せにたいするすべての利用可能 な情報です。

関心事項

Postgres のルールシステムは動かないと 考えられた長い期間がありました。ルールの使用は推奨されず、ビューの ルールは一部のみしか機能しませんでした。 SELECT 以外の他の文にルール システムが正しく適用されないため、これらのビューのルールは問題 (例えばビューのデータを使用した UPDATE が機能しない)を引き起こしま した。

この期間、開発は継続され数多くの機能がパーサとオプティマイザに 追加されました。ルールシステムは他の機能と同期をとることからどん どん外れて行き、そこからの修復がますます難しくなりました。そして だれもやらなくなりました。

6.4 のため、だれかが部屋に閉じこもり、深呼吸をして、そしてこの どうしようもないものをいじくり廻しました。そして出来上がったも のがこのドキュメントで解説した機能をもつルールシステムでした。 とはいっても、まだ扱われていない機能や現時点では Postgres の問合せオプティマイザに よってサポートされていない事によるいくつかの不都合もあります。

  • 集約したカラムをもっているビューは悪質な問題を抱えています。 条件の中の集約式は subselects で用いられなければ成りません。 現時点で、それぞれが集約カラムを持っていて、条件の中で 二つの集約値を比較する二つのビューのジョインを行うことは 不可能です。とりあえず、それらの集約式を適切な引数を持った 関数にしてビューの定義の中で使用すればこのことは可能です。

  • union のビューは今のところサポートされていません。簡単な SELECT を union に書き換えるのは簡単ですが、もしビューが更新作業 を行っているジョインの一部である場合多少難しくなります。

  • ビュー定義において ORDER BY 句はサポートされていません。

  • DISTINCT はビュー定義でサポートされていません。

SQL 構文中での制約でパーサが絶対に生成 することが出来ないパースツリー構成をオプティマイザが操作して はならないとする適切な理由はありません。著者は将来この項目 が無くなることを望みます。

実装による副作用

ビューを実装するために、説明したルールシステムを使用すると 妙な副作用がでます。

    al_bundy=> INSERT INTO shoe (shoename, sh_avail, slcolor)
    al_bundy->     VALUES ('sh5', 0, 'black');
    INSERT 20128 1
    al_bundy=> SELECT shoename, sh_avail, slcolor FROM shoe_data;
    shoename  |sh_avail|slcolor   
    ----------+--------+----------
    sh1       |       2|black     
    sh3       |       4|brown     
    sh2       |       0|black     
    sh4       |       3|brown     
    (4 rows)
興味深いこととして INSERT の返りコードはオブジェクト ID を 引き渡してくれて、一つの行が挿入されたことを示しますが、 その行は shoe_data には現れません。 データベースディレクトリを見ると、ビューのリレーション shoe に対するデータベースファイルは ここにおいて、データブロックを持っているようです。 そして、まさにこれがその現象です。

同様にして DELETE も発行することが出来、もし条件が付いて いなければ、行が既に削除されていて次回の vacuum でファイル サイズがゼロにリセットされます。

なぜこのように振る舞うのかと言うと、INSERT に対するパースツリー は shoe リレーションのどんな変数も参照 しないからです。目的リストには定数値しかありません。 ですから適用するルールが無く、変更なしで実行される結果、その 行が挿入されます。DELETE の場合も同じです。

この処理を変更するには、非 SELECT の問合せの振舞いを修正する ルールを定義します。次の節の論題です。