バージョンごとのドキュメント一覧

41.4. INSERTUPDATEDELETEについてのルール #

<title>Rules on <command>INSERT</command>, <command>UPDATE</command>, and <command>DELETE</command></title>

Rules that are defined on <command>INSERT</command>, <command>UPDATE</command>, and <command>DELETE</command> are significantly different from the view rules described in the previous sections. First, their <command>CREATE RULE</command> command allows more: INSERTUPDATEDELETEに定義するルールは前節で解説したビューのルールとはまったく異なります。 第一点として、これらのCREATE RULEコマンドでは以下を行うことができます。

Second, they don't modify the query tree in place. Instead they create zero or more new query trees and can throw away the original one. 第二点として、その場で問い合わせツリーを変更しません。 その代わりに新規の0個以上の問い合わせツリーを生成して、オリジナルを破棄することができます。

注意

In many cases, tasks that could be performed by rules on <command>INSERT</command>/<command>UPDATE</command>/<command>DELETE</command> are better done with triggers. Triggers are notationally a bit more complicated, but their semantics are much simpler to understand. Rules tend to have surprising results when the original query contains volatile functions: volatile functions may get executed more times than expected in the process of carrying out the rules. 多くの場合、INSERT/UPDATE/DELETEにおけるルールによって実行できるタスクは、トリガーで実行した方が良いでしょう。 トリガーは概念としては少し複雑ですが、意味を理解するにはとても単純です。 元の問い合わせにvolatile関数を含む場合、ルールは驚かせる結果を返すことがよくあります。(volatile関数はルールを遂行する過程で予期していた回数より多く実行されてしまうかもしれません)

Also, there are some cases that are not supported by these types of rules at all, notably including <literal>WITH</literal> clauses in the original query and multiple-assignment sub-<literal>SELECT</literal>s in the <literal>SET</literal> list of <command>UPDATE</command> queries. This is because copying these constructs into a rule query would result in multiple evaluations of the sub-query, contrary to the express intent of the query's author. また、これらのタイプのルールが全くサポートしない場合もあります。 特にWITH句を元の問い合わせに含む場合とUPDATE問い合わせのSETリストの中で複数列に代入するサブSELECTの場合です。 これはルール問い合わせにこれらの構造を複製すると副問い合わせを複数回評価し、問い合わせの作者が表現したかった意図と異なる結果となるためです。

41.4.1. 更新ルールの動作 #

<title>How Update Rules Work</title>

Keep the syntax:

CREATE [ OR REPLACE ] RULE name AS ON event
    TO table [ WHERE condition ]
    DO [ ALSO | INSTEAD ] { NOTHING | command | ( command ; command ... ) }

in mind. In the following, <firstterm>update rules</firstterm> means rules that are defined on <command>INSERT</command>, <command>UPDATE</command>, or <command>DELETE</command>. 上記の構文を覚えておいてください。 以下では、更新ルールINSERTUPDATEDELETEに定義されたルールを意味します。

Update rules get applied by the rule system when the result relation and the command type of a query tree are equal to the object and event given in the <command>CREATE RULE</command> command. For update rules, the rule system creates a list of query trees. Initially the query-tree list is empty. There can be zero (<literal>NOTHING</literal> key word), one, or multiple actions. To simplify, we will look at a rule with one action. This rule can have a qualification or not and it can be <literal>INSTEAD</literal> or <literal>ALSO</literal> (the default). 更新ルールは、問い合わせツリーの結果リレーションとコマンド種類がCREATE RULEで与えられるオブジェクトとイベントと等しい場合にルールシステムによって適用されます。 更新ルールに対してルールシステムは問い合わせツリーのリストを生成します。 最初は問い合わせツリーリストは空です。 0(NOTHINGキーワード)、1つまたは複数のアクションが有効です。 簡単にするため、ここでは1つのアクションのルールを取り上げます。 このルールは条件を持っていても持っていなくても構いませんし、またINSTEADALSO(デフォルト)のいずれかを取ることができます。

What is a rule qualification? It is a restriction that tells when the actions of the rule should be done and when not. This qualification can only reference the pseudorelations <literal>NEW</literal> and/or <literal>OLD</literal>, which basically represent the relation that was given as object (but with a special meaning). ルール条件とはどんなものでしょうか。 それはルールのアクションを行わなければならない時と、行ってはならない時を指定する条件です。 基本的に(特別な意味合いを持った)オブジェクトとして与えられるリレーションであるNEW疑似リレーションかOLD疑似リレーション、または、その両者のみをこの条件は参照することができます。

So we have three cases that produce the following query trees for a one-action rule. 1アクションのルールに対し、以下の問い合わせツリーを生成する3つの場合があります。

ALSOまたはINSTEADで条件がない場合。

the query tree from the rule action with the original query tree's qualification added 元の問い合わせツリーの条件が追加された、ルールアクションからの問い合わせツリー

条件付き、かつALSO

the query tree from the rule action with the rule qualification and the original query tree's qualification added ルール条件と元の問い合わせツリーの条件が追加された、ルールアクションからの問い合わせツリー

条件付き、かつINSTEAD

the query tree from the rule action with the rule qualification and the original query tree's qualification; and the original query tree with the negated rule qualification added ルール条件と元の問い合わせツリーの条件が追加された、ルールアクションからの問い合わせツリーとルール条件の否定条件が追加された元の問い合わせツリー

Finally, if the rule is <literal>ALSO</literal>, the unchanged original query tree is added to the list. Since only qualified <literal>INSTEAD</literal> rules already add the original query tree, we end up with either one or two output query trees for a rule with one action. 最後に、もしルールがALSOの場合、変更されていない元の問い合わせツリーがリストに付け加えられます。 条件付きのINSTEADルールのみが既に元の構文解析ツリーに追加をしているので、最後は1つのアクションを持つルールに対して1つもしくは2つの問い合わせツリーにたどり着きます。

For <literal>ON INSERT</literal> rules, the original query (if not suppressed by <literal>INSTEAD</literal>) is done before any actions added by rules. This allows the actions to see the inserted row(s). But for <literal>ON UPDATE</literal> and <literal>ON DELETE</literal> rules, the original query is done after the actions added by rules. This ensures that the actions can see the to-be-updated or to-be-deleted rows; otherwise, the actions might do nothing because they find no rows matching their qualifications. ON INSERTルールでは、元の問い合わせは、(INSTEADにより止められていない限り)ルールによって追加されたアクションの前に実行されます。 これにより、アクションは挿入された行(複数可)を参照することができます。 しかし、ON UPDATEON DELETEルールでは、元の問い合わせはルールによって追加されたアクションの後に実行されます。 これは、アクションが更新される予定の、または削除される予定の行を参照できることを保証します。 さもないと、条件に一致する行を見つけることができないためにアクションが作動しなくなる可能性が起こります。

The query trees generated from rule actions are thrown into the rewrite system again, and maybe more rules get applied resulting in additional or fewer query trees. So a rule's actions must have either a different command type or a different result relation than the rule itself is on, otherwise this recursive process will end up in an infinite loop. (Recursive expansion of a rule will be detected and reported as an error.) ルールアクションで生成された問い合わせツリーは、再度書き換えシステムに渡され、より多くのルールの適用を受けて問い合わせツリーが増やされたり減らされたりするかもしれません。 ですから、ルールのアクションはルール自身とは異なるコマンド種類か、異なる結果リレーションを持っていなければなりません。 さもないと、この再帰的処理により無限ループに陥ってしまいます。 (ルールの再帰展開は検知され、エラーとして報告されます。)

The query trees found in the actions of the <structname>pg_rewrite</structname> system catalog are only templates. Since they can reference the range-table entries for <literal>NEW</literal> and <literal>OLD</literal>, some substitutions have to be made before they can be used. For any reference to <literal>NEW</literal>, the target list of the original query is searched for a corresponding entry. If found, that entry's expression replaces the reference. Otherwise, <literal>NEW</literal> means the same as <literal>OLD</literal> (for an <command>UPDATE</command>) or is replaced by a null value (for an <command>INSERT</command>). Any reference to <literal>OLD</literal> is replaced by a reference to the range-table entry that is the result relation. pg_rewriteシステムカタログのアクションにある問い合わせツリーは単なるテンプレートです。 これらはNEWOLDに対する範囲テーブルの項目を参照することができるため、使用される前に何らかの置換措置がとられていなければなりません。 NEWを参照する全てに対し、元の問い合わせの目的リストは対応する項目があるかどうか検索されます。 項目が見つかった場合には、その項目式が参照を置き換えます。 項目がなかった場合、NEWOLDと同じ意味になる(UPDATEの場合)か、NULLによって置き換えられます(INSERTの場合)。 OLDに対する参照は全て結果リレーションである範囲テーブルの項目への参照に置き換えられます。

After the system is done applying update rules, it applies view rules to the produced query tree(s). Views cannot insert new update actions so there is no need to apply update rules to the output of view rewriting. 更新ルールの適用が終わると、システムはそこで作られた構文解析ツリーにビュールールを適用します。 ビューは、新しい更新アクションを挿入できないため、ビュー書き換えの結果に更新ルールを適用する必要はありません。

41.4.1.1. 最初のルール、ステップバイステップ #

<title>A First Rule Step by Step</title>

Say we want to trace changes to the <literal>sl_avail</literal> column in the <literal>shoelace_data</literal> relation. So we set up a log table and a rule that conditionally writes a log entry when an <command>UPDATE</command> is performed on <literal>shoelace_data</literal>. shoelace_dataリレーションのsl_avail列の変化を追跡してみたいと思います。 そこでログ用テーブルと、shoelace_dataに対して行われるUPDATEをログに記録するルールを用意しました。


CREATE TABLE shoelace_log (
    sl_name    text,          &#45;- shoelace changed
    sl_avail   integer,       &#45;- new available value
    log_who    text,          &#45;- who did it
    log_when   timestamp      &#45;- when
);

CREATE TABLE shoelace_log (
    sl_name    text,          -- 変更された靴紐
    sl_avail   integer,       -- 新しい現在値
    log_who    text,          -- 誰が行ったか
    log_when   timestamp      -- いつ行ったか
);

CREATE RULE log_shoelace AS ON UPDATE TO shoelace_data
    WHERE NEW.sl_avail <> OLD.sl_avail
    DO INSERT INTO shoelace_log VALUES (
                                    NEW.sl_name,
                                    NEW.sl_avail,
                                    current_user,
                                    current_timestamp
                                );

Now someone does: ここで誰かが以下を実行します。

UPDATE shoelace_data SET sl_avail = 6 WHERE sl_name = 'sl7';

and we look at the log table: ログテーブルを見てみましょう。

SELECT * FROM shoelace_log;

 sl_name | sl_avail | log_who | log_when
---------+----------+---------+----------------------------------
 sl7     |        6 | Al      | Tue Oct 20 16:14:45 1998 MET DST
(1 row)

That's what we expected. What happened in the background is the following. The parser created the query tree: 思った通りの結果が出ました。 以下に裏で何が起こったのかを説明します。 パーサがまず以下の構文解析ツリーを生成しました。

UPDATE shoelace_data SET sl_avail = 6
  FROM shoelace_data shoelace_data
 WHERE shoelace_data.sl_name = 'sl7';

There is a rule <literal>log_shoelace</literal> that is <literal>ON UPDATE</literal> with the rule qualification expression: ルールlog_shoelaceには、ON UPDATEを持つルール条件式

NEW.sl_avail <> OLD.sl_avail

and the action: と以下のアクションがあります。

INSERT INTO shoelace_log VALUES (
       new.sl_name, new.sl_avail,
       current_user, current_timestamp )
  FROM shoelace_data new, shoelace_data old;

(This looks a little strange since you cannot normally write <literal>INSERT ... VALUES ... FROM</literal>. The <literal>FROM</literal> clause here is just to indicate that there are range-table entries in the query tree for <literal>new</literal> and <literal>old</literal>. These are needed so that they can be referenced by variables in the <command>INSERT</command> command's query tree.) (通常、INSERT ... VALUES ... FROMを書くことはできないのでちょっと奇妙に見えるかもしれません。 ここのFROM句は単にnewoldの問い合わせツリーの範囲テーブル項目があることを示しているだけです。 これらは、INSERTコマンドの問い合わせツリー中の変数から参照されるために必要なのです。)

The rule is a qualified <literal>ALSO</literal> rule, so the rule system has to return two query trees: the modified rule action and the original query tree. In step 1, the range table of the original query is incorporated into the rule's action query tree. This results in: このルールは条件付きのALSOルールですので、ルールシステムは変更されたルールアクションと元の問い合わせツリーという2つの問い合わせツリーを返さなければなりません。 第1の段階で元の問い合わせの範囲テーブルはルールアクション問い合わせツリーに取り込まれます。 そして、次の結果を生みます。

INSERT INTO shoelace_log VALUES (
       new.sl_name, new.sl_avail,
       current_user, current_timestamp )
  FROM shoelace_data new, shoelace_data old,
       shoelace_data shoelace_data;

In step 2, the rule qualification is added to it, so the result set is restricted to rows where <literal>sl_avail</literal> changes: 第2段階で、以下のようにルール条件が付け加えられます。 これにより、この結果集合はsl_availが変更した行に限定されます。

INSERT INTO shoelace_log VALUES (
       new.sl_name, new.sl_avail,
       current_user, current_timestamp )
  FROM shoelace_data new, shoelace_data old,
       shoelace_data shoelace_data
 WHERE new.sl_avail <> old.sl_avail;

(This looks even stranger, since <literal>INSERT ... VALUES</literal> doesn't have a <literal>WHERE</literal> clause either, but the planner and executor will have no difficulty with it. They need to support this same functionality anyway for <literal>INSERT ... SELECT</literal>.) INSERT ... VALUESWHERE句を持たないため、これはさらに奇妙です。 しかし、プランナとエグゼキュータには問題はありません。 これらはどのみちINSERT ... SELECTのために同じ機能をサポートしなければなりません。)

In step 3, the original query tree's qualification is added, restricting the result set further to only the rows that would have been touched by the original query: 第3段階で、以下のように元の問い合わせツリーの条件が付け加えられ、結果集合は元の問い合わせで変更された行のみにさらに限定されます。

INSERT INTO shoelace_log VALUES (
       new.sl_name, new.sl_avail,
       current_user, current_timestamp )
  FROM shoelace_data new, shoelace_data old,
       shoelace_data shoelace_data
 WHERE new.sl_avail <> old.sl_avail
   AND shoelace_data.sl_name = 'sl7';

Step 4 replaces references to <literal>NEW</literal> by the target list entries from the original query tree or by the matching variable references from the result relation: 第4段階では、以下のように元の問い合わせツリーの目的リスト項目、または結果リレーションの該当する変数参照で、NEWの参照を置換します。

INSERT INTO shoelace_log VALUES (
       shoelace_data.sl_name, 6,
       current_user, current_timestamp )
  FROM shoelace_data new, shoelace_data old,
       shoelace_data shoelace_data
 WHERE 6 <> old.sl_avail
   AND shoelace_data.sl_name = 'sl7';

Step 5 changes <literal>OLD</literal> references into result relation references: 第5段階は、以下のようにOLD参照を結果リレーション参照に置き換えます。

INSERT INTO shoelace_log VALUES (
       shoelace_data.sl_name, 6,
       current_user, current_timestamp )
  FROM shoelace_data new, shoelace_data old,
       shoelace_data shoelace_data
 WHERE 6 <> shoelace_data.sl_avail
   AND shoelace_data.sl_name = 'sl7';

That's it. Since the rule is <literal>ALSO</literal>, we also output the original query tree. In short, the output from the rule system is a list of two query trees that correspond to these statements: これで終わりです。このルールはALSOのため、元の問い合わせツリーも出力します。 まとめると、ルールシステムからの出力は以下の文に対応する2つの問い合わせツリーのリストです。

INSERT INTO shoelace_log VALUES (
       shoelace_data.sl_name, 6,
       current_user, current_timestamp )
  FROM shoelace_data
 WHERE 6 <> shoelace_data.sl_avail
   AND shoelace_data.sl_name = 'sl7';

UPDATE shoelace_data SET sl_avail = 6
 WHERE sl_name = 'sl7';

These are executed in this order, and that is exactly what the rule was meant to do. この2つは順番通りに処理され、正確にルールが定義した通りです。

The substitutions and the added qualifications ensure that, if the original query would be, say: 元の問い合わせが例えば下記のような場合に、置換と追加された条件は、ログには何も書かれないことを確実にします。

UPDATE shoelace_data SET sl_color = 'green'
 WHERE sl_name = 'sl7';

no log entry would get written. In that case, the original query tree does not contain a target list entry for <literal>sl_avail</literal>, so <literal>NEW.sl_avail</literal> will get replaced by <literal>shoelace_data.sl_avail</literal>. Thus, the extra command generated by the rule is: この場合、元の問い合わせツリーの目的リストにはsl_availの項目がありませんので、NEW.sl_availshoelace_data.sl_availに置き換えられます。 その結果、このルールによって以下のような特別の問い合わせが生成されます。

INSERT INTO shoelace_log VALUES (
       shoelace_data.sl_name, shoelace_data.sl_avail,
       current_user, current_timestamp )
  FROM shoelace_data
 WHERE shoelace_data.sl_avail <> shoelace_data.sl_avail
   AND shoelace_data.sl_name = 'sl7';

and that qualification will never be true. そしてこの条件は決して真にはなりません。

It will also work if the original query modifies multiple rows. So if someone issued the command: もし元の問い合わせが複数の行を変更してもうまくいきます。 ですから、誰かが下記のようなコマンドを実行したとします。

UPDATE shoelace_data SET sl_avail = 0
 WHERE sl_color = 'black';

four rows in fact get updated (<literal>sl1</literal>, <literal>sl2</literal>, <literal>sl3</literal>, and <literal>sl4</literal>). But <literal>sl3</literal> already has <literal>sl_avail = 0</literal>. In this case, the original query trees qualification is different and that results in the extra query tree: この場合、実際には4行が更新されます(sl1sl2sl3およびsl4)。 しかしsl3は既にsl_avail = 0を持っています。 この場合、元の問い合わせツリーの条件を満たさず、その結果、以下のような特別の問い合わせツリーがルールによって生成されます。

INSERT INTO shoelace_log
SELECT shoelace_data.sl_name, 0,
       current_user, current_timestamp
  FROM shoelace_data
 WHERE 0 <> shoelace_data.sl_avail
   AND shoelace_data.sl_color = 'black';

being generated by the rule. This query tree will surely insert three new log entries. And that's absolutely correct. この構文解析ツリーは確実に3つの新しいログ項目を挿入します。 これはまったく正しい動作です [訳注:sl3行はWHERE 0 != shoelace_data.sl_avail条件を満たさない(0!=0)ので、実際に更新される4行-1の3行分のログ項目が挿入されます]。

Here we can see why it is important that the original query tree is executed last. If the <command>UPDATE</command> had been executed first, all the rows would have already been set to zero, so the logging <command>INSERT</command> would not find any row where <literal>0 &lt;&gt; shoelace_data.sl_avail</literal>. ここで元の構文解析ツリーが最後に実行されるということが重要な理由がわかります。 もしUPDATEが先に実行されたとしたら、全ての行は0にセットされ、0 <> shoelace_data.sl_availである行をログ書き込み時のINSERTの段階で見つけられなくなります。

41.4.2. ビューとの協調 #

<title>Cooperation with Views</title>

A simple way to protect view relations from the mentioned possibility that someone can try to run <command>INSERT</command>, <command>UPDATE</command>, or <command>DELETE</command> on them is to let those query trees get thrown away. So we could create the rules: 誰かがビューに対してINSERTUPDATEDELETEを発行するといった、前述の可能性からビューリレーションを保護する簡単な方法は、それらの構文解析ツリーを破棄してしまうことです。 このために以下のルールを作ることができます。

CREATE RULE shoe_ins_protect AS ON INSERT TO shoe
    DO INSTEAD NOTHING;
CREATE RULE shoe_upd_protect AS ON UPDATE TO shoe
    DO INSTEAD NOTHING;
CREATE RULE shoe_del_protect AS ON DELETE TO shoe
    DO INSTEAD NOTHING;

If someone now tries to do any of these operations on the view relation <literal>shoe</literal>, the rule system will apply these rules. Since the rules have no actions and are <literal>INSTEAD</literal>, the resulting list of query trees will be empty and the whole query will become nothing because there is nothing left to be optimized or executed after the rule system is done with it. 誰かがshoeビューリレーションに上記の操作を行おうとすると、ルールシステムはルールを適用します。 ルールにはアクションがなく、かつ、INSTEADですから、結果の問い合わせツリーリストは空になります。 ルールシステムの処理が完了した後に最適化されるものや実行されるべきものが何も残っていませんので、問い合わせ全体が無効になります。

A more sophisticated way to use the rule system is to create rules that rewrite the query tree into one that does the right operation on the real tables. To do that on the <literal>shoelace</literal> view, we create the following rules: より洗練されたルールシステムの使用方法は、実テーブルに適当な操作を行う問い合わせツリーへの書き換えを行うルールを作ることです。 shoelaceビューにこれを適用するために以下のルールを作ります。

CREATE RULE shoelace_ins AS ON INSERT TO shoelace
    DO INSTEAD
    INSERT INTO shoelace_data VALUES (
           NEW.sl_name,
           NEW.sl_avail,
           NEW.sl_color,
           NEW.sl_len,
           NEW.sl_unit
    );

CREATE RULE shoelace_upd AS ON UPDATE TO shoelace
    DO INSTEAD
    UPDATE shoelace_data
       SET sl_name = NEW.sl_name,
           sl_avail = NEW.sl_avail,
           sl_color = NEW.sl_color,
           sl_len = NEW.sl_len,
           sl_unit = NEW.sl_unit
     WHERE sl_name = OLD.sl_name;

CREATE RULE shoelace_del AS ON DELETE TO shoelace
    DO INSTEAD
    DELETE FROM shoelace_data
     WHERE sl_name = OLD.sl_name;

If you want to support <literal>RETURNING</literal> queries on the view, you need to make the rules include <literal>RETURNING</literal> clauses that compute the view rows. This is usually pretty trivial for views on a single table, but it's a bit tedious for join views such as <literal>shoelace</literal>. An example for the insert case is: このビュー上でRETURNING問い合わせをサポートしたい場合、ビューの行を計算するRETURNING句を含むルールを作成しなければなりません。 これは通常、単一テーブルに対するビューでは非常に簡単ですが、shoelaceのような結合されたビューの場合は多少やっかいです。 挿入する場合を例として以下に示します。

CREATE RULE shoelace_ins AS ON INSERT TO shoelace
    DO INSTEAD
    INSERT INTO shoelace_data VALUES (
           NEW.sl_name,
           NEW.sl_avail,
           NEW.sl_color,
           NEW.sl_len,
           NEW.sl_unit
    )
    RETURNING
           shoelace_data.*,
           (SELECT shoelace_data.sl_len * u.un_fact
            FROM unit u WHERE shoelace_data.sl_unit = u.un_name);

Note that this one rule supports both <command>INSERT</command> and <command>INSERT RETURNING</command> queries on the view &mdash; the <literal>RETURNING</literal> clause is simply ignored for <command>INSERT</command>. この1つのルールが、ビューに対するINSERT問い合わせとINSERT RETURNING問い合わせルールをサポートすることに注意してください。 INSERTではRETURNING句が無視されるだけです。

Now assume that once in a while, a pack of shoelaces arrives at the shop and a big parts list along with it. But you don't want to manually update the <literal>shoelace</literal> view every time. Instead we set up two little tables: one where you can insert the items from the part list, and one with a special trick. The creation commands for these are: ここで店には不定期に靴紐のケースが分厚い送り状とともに届けられると仮定します。 しかし、毎回毎回手作業でshoelaceビューを更新したくはありません。 代わりに、送り状から品目を挿入するテーブルと特殊な仕掛けを持つテーブルの2つの小さなテーブルを用意します。 以下はそれらを作成するコマンドです。

CREATE TABLE shoelace_arrive (
    arr_name    text,
    arr_quant   integer
);

CREATE TABLE shoelace_ok (
    ok_name     text,
    ok_quant    integer
);

CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok
    DO INSTEAD
    UPDATE shoelace
       SET sl_avail = sl_avail + NEW.ok_quant
     WHERE sl_name = NEW.ok_name;

Now you can fill the table <literal>shoelace_arrive</literal> with the data from the parts list: これで、送り状のデータをshoelace_arriveテーブルに投入することができます。

SELECT * FROM shoelace_arrive;

 arr_name | arr_quant
----------+-----------
 sl3      |        10
 sl6      |        20
 sl8      |        20
(3 rows)

Take a quick look at the current data: そして現在のデータをチェックします。

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      |        6 | 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)

Now move the arrived shoelaces in: さて、届いた靴紐を移動します。

INSERT INTO shoelace_ok SELECT * FROM shoelace_arrive;

and check the results: そして結果を確認します。

SELECT * FROM shoelace ORDER BY sl_name;

 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      |        6 | brown    |     60 | cm      |        60
 sl4      |        8 | black    |     40 | inch    |     101.6
 sl3      |       10 | black    |     35 | inch    |      88.9
 sl8      |       21 | brown    |     40 | inch    |     101.6
 sl5      |        4 | brown    |      1 | m       |       100
 sl6      |       20 | brown    |    0.9 | m       |        90
(8 rows)

SELECT * FROM shoelace_log;

 sl_name | sl_avail | log_who| log_when
---------+----------+--------+----------------------------------
 sl7     |        6 | Al     | Tue Oct 20 19:14:45 1998 MET DST
 sl3     |       10 | Al     | Tue Oct 20 19:25:16 1998 MET DST
 sl6     |       20 | Al     | Tue Oct 20 19:25:16 1998 MET DST
 sl8     |       21 | Al     | Tue Oct 20 19:25:16 1998 MET DST
(4 rows)

It's a long way from the one <literal>INSERT ... SELECT</literal> to these results. And the description of the query-tree transformation will be the last in this chapter. First, there is the parser's output: 1つのINSERT ... SELECTからこの結果になるには、長い道のりがあります。 本章での問い合わせツリーの変形に関する説明はこれが最後です。 まず、以下のようなパーサの出力があります。

INSERT INTO shoelace_ok
SELECT shoelace_arrive.arr_name, shoelace_arrive.arr_quant
  FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok;

Now the first rule <literal>shoelace_ok_ins</literal> is applied and turns this into: 最初のshoelace_ok_insルールが適用され、結果は以下のようになります。

UPDATE shoelace
   SET sl_avail = shoelace.sl_avail + shoelace_arrive.arr_quant
  FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
       shoelace_ok old, shoelace_ok new,
       shoelace shoelace
 WHERE shoelace.sl_name = shoelace_arrive.arr_name;

and throws away the original <command>INSERT</command> on <literal>shoelace_ok</literal>. This rewritten query is passed to the rule system again, and the second applied rule <literal>shoelace_upd</literal> produces: そして、shoelace_okに対する元のINSERTを破棄します。 この書き換えられた問い合わせは再びルールシステムに渡されて、2番目に適用されるshoelace_updルールは以下を生成します。

UPDATE shoelace_data
   SET sl_name = shoelace.sl_name,
       sl_avail = shoelace.sl_avail + shoelace_arrive.arr_quant,
       sl_color = shoelace.sl_color,
       sl_len = shoelace.sl_len,
       sl_unit = shoelace.sl_unit
  FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
       shoelace_ok old, shoelace_ok new,
       shoelace shoelace, shoelace old,
       shoelace new, shoelace_data shoelace_data
 WHERE shoelace.sl_name = shoelace_arrive.arr_name
   AND shoelace_data.sl_name = shoelace.sl_name;

Again it's an <literal>INSTEAD</literal> rule and the previous query tree is trashed. Note that this query still uses the view <literal>shoelace</literal>. But the rule system isn't finished with this step, so it continues and applies the <literal>_RETURN</literal> rule on it, and we get: これは再びINSTEADルールですので、以前の問い合わせツリーは破棄されます。 この問い合わせはshoelaceビューを引き続き使用していることに注意してください。 しかし、この段階ではルールシステムは終了していないため、引き続き_RETURNルールが適用され、下記のようになります。

UPDATE shoelace_data
   SET sl_name = s.sl_name,
       sl_avail = s.sl_avail + shoelace_arrive.arr_quant,
       sl_color = s.sl_color,
       sl_len = s.sl_len,
       sl_unit = s.sl_unit
  FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
       shoelace_ok old, shoelace_ok new,
       shoelace shoelace, shoelace old,
       shoelace new, shoelace_data shoelace_data,
       shoelace old, shoelace new,
       shoelace_data s, unit u
 WHERE s.sl_name = shoelace_arrive.arr_name
   AND shoelace_data.sl_name = s.sl_name;

Finally, the rule <literal>log_shoelace</literal> gets applied, producing the extra query tree: 最後に、log_shoelaceルールが適用され、以下のような特別な問い合わせツリーが生成されます。

INSERT INTO shoelace_log
SELECT s.sl_name,
       s.sl_avail + shoelace_arrive.arr_quant,
       current_user,
       current_timestamp
  FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
       shoelace_ok old, shoelace_ok new,
       shoelace shoelace, shoelace old,
       shoelace new, shoelace_data shoelace_data,
       shoelace old, shoelace new,
       shoelace_data s, unit u,
       shoelace_data old, shoelace_data new
       shoelace_log shoelace_log
 WHERE s.sl_name = shoelace_arrive.arr_name
   AND shoelace_data.sl_name = s.sl_name
   AND (s.sl_avail + shoelace_arrive.arr_quant) <> s.sl_avail;

After that the rule system runs out of rules and returns the generated query trees. この後、ルールシステムはルールを使い切り、生成された問い合わせツリーを返します。

So we end up with two final query trees that are equivalent to the <acronym>SQL</acronym> statements: そして、以下のSQL文と等価となる2つの最終問い合わせツリーで終結します。

INSERT INTO shoelace_log
SELECT s.sl_name,
       s.sl_avail + shoelace_arrive.arr_quant,
       current_user,
       current_timestamp
  FROM shoelace_arrive shoelace_arrive, shoelace_data shoelace_data,
       shoelace_data s
 WHERE s.sl_name = shoelace_arrive.arr_name
   AND shoelace_data.sl_name = s.sl_name
   AND s.sl_avail + shoelace_arrive.arr_quant <> s.sl_avail;

UPDATE shoelace_data
   SET sl_avail = shoelace_data.sl_avail + shoelace_arrive.arr_quant
  FROM shoelace_arrive shoelace_arrive,
       shoelace_data shoelace_data,
       shoelace_data s
 WHERE s.sl_name = shoelace_arrive.sl_name
   AND shoelace_data.sl_name = s.sl_name;

The result is that data coming from one relation inserted into another, changed into updates on a third, changed into updating a fourth plus logging that final update in a fifth gets reduced into two queries. 結果は、1つのリレーションから来たデータが別のリレーションに挿入され、3つ目のリレーションの更新に変更され、4つ目の更新と5つ目への最終更新のログ記録に変更され、最終的に2つの問い合わせに縮小されます。

There is a little detail that's a bit ugly. Looking at the two queries, it turns out that the <literal>shoelace_data</literal> relation appears twice in the range table where it could definitely be reduced to one. The planner does not handle it and so the execution plan for the rule systems output of the <command>INSERT</command> will be ちょっと見苦しい小さな事項があります。 でき上がった2つの問い合わせを見ると、1つに縮小されたはずのshoelace_dataリレーションが範囲テーブルに二度出てきます。 プランナは処理をしないので、INSERTのルールシステムの出力に対する実行計画は次のようになります。

Nested Loop
  ->  Merge Join
        ->  Seq Scan
              ->  Sort
                    ->  Seq Scan on s
        ->  Seq Scan
              ->  Sort
                    ->  Seq Scan on shoelace_arrive
  ->  Seq Scan on shoelace_data

while omitting the extra range table entry would result in a 一方、余計な範囲テーブル項目を省略することで、以下のようにログテーブルにまったく同じ項目が作られます。

Merge Join
  ->  Seq Scan
        ->  Sort
              ->  Seq Scan on s
  ->  Seq Scan
        ->  Sort
              ->  Seq Scan on shoelace_arrive

which produces exactly the same entries in the log table. Thus, the rule system caused one extra scan on the table <literal>shoelace_data</literal> that is absolutely not necessary. And the same redundant scan is done once more in the <command>UPDATE</command>. But it was a really hard job to make that all possible at all. ですから、ルールシステムは、まったく必要のないshoelace_dataリレーションに対する余計なスキャンを一度行うことになります。 そしてUPDATEでも同様な不要なスキャンが再度実行されます。 しかしながら、これらを全て可能にするのは大変な仕事です。

Now we make a final demonstration of the <productname>PostgreSQL</productname> rule system and its power. Say you add some shoelaces with extraordinary colors to your database: 最後にPostgreSQLのルールシステムとその効力を示しましょう。 例えば、まったく売れそうもない靴紐をデータベースに追加してみます。

INSERT INTO shoelace VALUES ('sl9', 0, 'pink', 35.0, 'inch', 0.0);
INSERT INTO shoelace VALUES ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0);

We would like to make a view to check which <literal>shoelace</literal> entries do not fit any shoe in color. The view for this is: 全ての靴に合わない色がshoelace項目にあるかどうかを検査するビューを作成したいと考えます。 ビューは以下のようになります。

CREATE VIEW shoelace_mismatch AS
    SELECT * FROM shoelace WHERE NOT EXISTS
        (SELECT shoename FROM shoe WHERE slcolor = sl_color);

Its output is: この出力は以下のようになります。

SELECT * FROM shoelace_mismatch;

 sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm
---------+----------+----------+--------+---------+-----------
 sl9     |        0 | pink     |     35 | inch    |      88.9
 sl10    |     1000 | magenta  |     40 | inch    |     101.6

Now we want to set it up so that mismatching shoelaces that are not in stock are deleted from the database. To make it a little harder for <productname>PostgreSQL</productname>, we don't delete it directly. Instead we create one more view: ここで、合う靴がない靴紐のうち、在庫がないものをデータベースから削除するように設定してみます。 これはPostgreSQLでは困難な作業ですので、直接削除しません。 代わりに、以下のようにもう1つビューを作成します。

CREATE VIEW shoelace_can_delete AS
    SELECT * FROM shoelace_mismatch WHERE sl_avail = 0;

and do it this way: そして、以下を行います。

DELETE FROM shoelace WHERE EXISTS
    (SELECT * FROM shoelace_can_delete
             WHERE sl_name = shoelace.sl_name);

The results are: 結果は次のとおりです。

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     |        6 | brown    |     60 | cm      |        60
 sl4     |        8 | black    |     40 | inch    |     101.6
 sl3     |       10 | black    |     35 | inch    |      88.9
 sl8     |       21 | brown    |     40 | inch    |     101.6
 sl10    |     1000 | magenta  |     40 | inch    |     101.6
 sl5     |        4 | brown    |      1 | m       |       100
 sl6     |       20 | brown    |    0.9 | m       |        90
(9 rows)

A <command>DELETE</command> on a view, with a subquery qualification that in total uses 4 nesting/joined views, where one of them itself has a subquery qualification containing a view and where calculated view columns are used, gets rewritten into one single query tree that deletes the requested data from a real table. 合計4つのネスト/結合されたビューを副問い合わせの条件として持ち、その中の1つはビューを含む副問い合わせ条件を持ち、かつ演算を施されたビューの列が使われる場合の、ビューに対するDELETEが、実テーブルから要求されたデータを削除する単一の問い合わせツリーに書き換えられます。

There are probably only a few situations out in the real world where such a construct is necessary. But it makes you feel comfortable that it works. このような構造が必要な状況は実社会ではほとんどないと思われます。 しかし、実際に動くことを確認できれば安心できます。