PL/Tcl

PL/Tcl is a loadable procedural language for the Postgres database system that enables the Tcl language to be used to create functions and trigger-procedures.

PL/Tcl は Postgres データベースシステム 用の読み込み可能な手続き言語であり、Tcl 言語を使った関数とトリガ プロシージャを作成できます。

This package was originally written by Jan Wieck.

このパッケージはJan Wieck氏によって記述されたものを元にしています。

概要

PL/Tcl offers most of the capabilities a function writer has in the C language, except for some restrictions.

PL/Tcl は、いくつかの制限を除き、C 言語で記述された関数が持つ機能 のほとんどを提供します。

The good restriction is, that everything is executed in a safe Tcl-interpreter. In addition to the limited command set of safe Tcl, only a few commands are available to access the database over SPI and to raise messages via elog(). There is no way to access internals of the database backend or gaining OS-level access under the permissions of the Postgres user ID like in C. Thus, any unprivileged database user may be permitted to use this language.

全てが安全な Tcl インタプリタで実行されることは、良い制約になって います。安全な Tcl の制限付きのコマンドセットに加えて、SPI 経由で のデータベースへのアクセス、elog() 経由でのメッセージの出力を行な うための少ないコマンドも使用できます。データベースバックエンドの内 部へのアクセス、または、C のように Postgres ユーザ ID の権利を元にした OS レベルのアクセス手段の入手を行なうことはできません。従って、特権を 持たないデータベースユーザもこの言語を使うことができます。

The other, internal given, restriction is, that Tcl procedures cannot be used to create input-/output-functions for new data types.

この他の、内部的に付与される制限は、Tcl プロシージャは新しいデータ 型用の入出力関数を作成するために使うことができないということです。

The shared object for the PL/Tcl call handler is automatically built and installed in the Postgres library directory if the Tcl/Tk support is specified in the configuration step of the installation procedure.

インストール処理における設定段階にて Tcl/Tk サポートを指定した場合、 PL/Tcl 呼び出しハンドラ用の共有オブジェクトは自動的に構築され Postgres ライブラリディレクトリにインスト ールされます。

説明

Postgres 関数と Tcl プロシージャ名

In Postgres, one and the same function name can be used for different functions as long as the number of arguments or their types differ. This would collide with Tcl procedure names. To offer the same flexibility in PL/Tcl, the internal Tcl procedure names contain the object ID of the procedures pg_proc row as part of their name. Thus, different argtype versions of the same Postgres function are different for Tcl too.

Postgres では、異なる引数数または異なる 引数型を持つ限り、同一名の関数を異なる関数として使用できます。これ は Tcl プロシージャ名と相反します。PL/Tcl で同じ柔軟性を提供するた めに、内部的な Tcl プロシージャ名はそのプロシージャに対応する pg_proc の行のオブジェクト ID をその名前の一部に含んでいます。従っ て、同じ Postgres 関数の異なる引数型を持 つバージョンは Tcl でも異なるものになります。

PL/Tcl における関数の定義

To create a function in the PL/Tcl language, use the known syntax

PL/Tcl言語で関数を定義するには、次のような既知の文法を使います。

    CREATE FUNCTION funcname (argument-types) RETURNS returntype AS '
        # PL/Tcl function body
    ' LANGUAGE 'pltcl';
    
When calling this function in a query, the arguments are given as variables $1 ... $n to the Tcl procedure body. So a little max function returning the higher of two int4 values would be created as: 問い合わせ内でこの関数を呼ぶ時、引数は $1 ... $n という変数として、 Tcl プロシージャの本体に与えられます。ですので、2 つの int4 値の うちどちらが大きいかを返す、小さな最大値算出関数は次のように作成 されます。
    CREATE FUNCTION tcl_max (int4, int4) RETURNS int4 AS '
        if {$1 > $2} {return $1}
	return $2
    ' LANGUAGE 'pltcl';
    
Composite type arguments are given to the procedure as Tcl arrays. The element names in the array are the attribute names of the composite type. If an attribute in the actual row has the NULL value, it will not appear in the array! Here is an example that defines the overpaid_2 function (as found in the older Postgres documentation) in PL/Tcl 複合型引数は Tcl の配列として、プロシージャに与えられます。配列の 要素名は複合型の属性名になります。実際の行のある属性が NULL 値であ った場合、その配列には現れません!(古い Postgres 文書にあった)overpaid_2 関数を PL/Tcl で定義した例をここに示します。
    CREATE FUNCTION overpaid_2 (EMP) RETURNS bool AS '
        if {200000.0 < $1(salary)} {
            return "t"
        }
        if {$1(age) < 30 && 100000.0 < $1(salary)} {
            return "t"
        }
        return "f"
    ' LANGUAGE 'pltcl';
    

PL/Tcl におけるグローバルデータ

Sometimes (especially when using the SPI functions described later) it is useful to have some global status data that is held between two calls to a procedure. All PL/Tcl procedures executed in one backend share the same safe Tcl interpreter. To help protecting PL/Tcl procedures from side effects, an array is made available to each procedure via the upvar command. The global name of this variable is the procedures internal name and the local name is GD.

あるプロシージャを呼び出す 2 つの間で保持されるグローバルな状態を 示すデータを幾つか持つことが有益な時(特に、後述の SPI 関数を使用 した時)があります。全ての PL/Tcl プロシージャは、1 つのバックエン ドが同じ安全な Tcl インタプリタを共有して実行されます。PL/Tcl プロ シージャを外部から保護することを補助するために、upvar コマンド経由 で各プロシージャが使用できる配列が作成されます。この変数のグローバ ルな名前はそのプロシージャの内部的な名前であり、ローカルな名前は GD です。

PL/Tcl におけるトリガプロシージャ

Trigger procedures are defined in Postgres as functions without arguments and a return type of opaque. And so are they in the PL/Tcl language.

トリガプロシージャは Postgres 内で、 引数の無い、opaque 型を返す関数として定義されます。PL/Tcl 言語の そういった関数もトリガプロシージャとなります。

The informations from the trigger manager are given to the procedure body in the following variables:

トリガマネージャからの情報は次の変数によって、プロシージャ本体に 与えられます。

$TG_name

The name of the trigger from the CREATE TRIGGER statement.

CREATE TRIGGER 文でのトリガの名前

$TG_relid

The object ID of the table that caused the trigger procedure to be invoked.

トリガプロシージャの呼び出し元となるテーブルのオブジェクト ID。

$TG_relatts

A Tcl list of the tables field names prefixed with an empty list element. So looking up an element name in the list with the lsearch Tcl command returns the same positive number starting from 1 as the fields are numbered in the pg_attribute system catalog.

空のリスト要素を前に付けたテーブルのフィールド名の Tcl リスト。 Tcl の lsearch コマンドを使ってそのリストの要素名を検索すると、 1 から始まる、pg_attribute システムカタログにおけるそのフィール ドの番号と同じ、正の値が返ります。

$TG_when

The string BEFORE or AFTER depending on the event of the trigger call.

トリガ呼び出しのイベントに依存した BEFORE または AFTER とい う文字列。

$TG_level

The string ROW or STATEMENT depending on the event of the trigger call.

トリガ呼び出しのイベントに依存した ROW または STATEMENT とい う文字列。

$TG_op

The string INSERT, UPDATE or DELETE depending on the event of the trigger call.

トリガ呼び出しのイベントに依存した INSERT、UPDATE または DELETE という文字列。

$NEW

An array containing the values of the new table row on INSERT/UPDATE actions, or empty on DELETE.

INSERT/UPDATE 動作時のテーブルの新しい行の値を持つ配列。DELETE の 場合は空となります。

$OLD

An array containing the values of the old table row on UPDATE/DELETE actions, or empty on INSERT.

UPDATE/DELETE 動作時のテーブルの古い行の値をもつ配列。INSERT の場 合は空になります。

$GD

The global status data array as described above.

上述のグローバルな状態を示すデータ配列。

$args

A Tcl list of the arguments to the procedure as given in the CREATE TRIGGER statement. The arguments are also accessible as $1 ... $n in the procedure body.

CREATE TRIGGER 文で指定されたプロシージャの引数の Tcl リスト。引数 はプロシージャ本体で $1 ... $n としても参照できます。

The return value from a trigger procedure is one of the strings OK or SKIP, or a list as returned by the 'array get' Tcl command. If the return value is OK, the normal operation (INSERT/UPDATE/DELETE) that fired this trigger will take place. Obviously, SKIP tells the trigger manager to silently suppress the operation. The list from 'array get' tells PL/Tcl to return a modified row to the trigger manager that will be inserted instead of the one given in $NEW (INSERT/UPDATE only). Needless to say that all this is only meaningful when the trigger is BEFORE and FOR EACH ROW.

トリガプロシージャからの戻り値は OK または SKIP という文字列、 または、Tcl の 'array get' コマンドにより返されるリストのいずれか です。戻り値が OK ならば、このトリガを発行した通常の( INSERT/ UPDATE/DELETE といった)操作は有効になります。反対に、SKIP ならば、 トリガマネージャに黙ってその操作を差し止めるように通知します。 'array get' から得られるリストは、PL/Tcl によって変更され、$NEW と して与えられたものの代わりに挿入される行を、トリガマネージャに 返すように通知します(INSERT/UPDATEのみです)。いうまでもなく、これ ら全ては、トリガが BEFORE か FOR EACH ROW の場合にのみ意味があり ます。

Here's a little example trigger procedure that forces an integer value in a table to keep track of the # of updates that are performed on the row. For new row's inserted, the value is initialized to 0 and then incremented on every update operation:

テーブル内のある整数値を行へ行なわれた更新回数を保持するようにさ せるトリガプロシージャの小さな例をここに示します。新しい行の挿 入の場合は、この値は 0 に初期化され、その後の更新操作時にインクリ メントされます。

    CREATE FUNCTION trigfunc_modcount() RETURNS OPAQUE AS '
        switch $TG_op {
            INSERT {
                set NEW($1) 0
            }
            UPDATE {
                set NEW($1) $OLD($1)
                incr NEW($1)
            }
            default {
                return OK
            }
        }
        return [array get NEW]
    ' LANGUAGE 'pltcl';

    CREATE TABLE mytab (num int4, modcnt int4, desc text);

    CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
        FOR EACH ROW EXECUTE PROCEDURE trigfunc_modcount('modcnt');
    

PL/Tcl からのデータベースへのアクセス

The following commands are available to access the database from the body of a PL/Tcl procedure:

PL/Tcl プロシージャ本体からデータベースにアクセスできるようにす るコマンドには以下のものがあります。

elog レベル メッセージ

Fire a log message. Possible levels are NOTICE, WARN, ERROR, FATAL, DEBUG and NOIND like for the elog() C function.

ログメッセージを発行します。レベルとして C の elog() 関数と同様 に、NOTICE、WARN、ERROR、FATAL、DEBUG、NOIND を指定できます。

quote 文字列

Duplicates all occurences of single quote and backslash characters. It should be used when variables are used in the query string given to spi_exec or spi_prepare (not for the value list on spi_execp). Think about a query string like

    "SELECT '$val' AS ret"
    
where the Tcl variable val actually contains "doesn't". This would result in the final query string
    "SELECT 'doesn't' AS ret"
    
what would cause a parse error during spi_exec or spi_prepare. It should contain
    "SELECT 'doesn''t' AS ret"
    
and has to be written as
    "SELECT '[ quote $val ]' AS ret"
    

現れる全てのシングルクォートとバックスラッシュ文字を二重にします。 変数が、spi_exec または spi_prepare へ与えられる問い合わせ用文字列と して使われる場合、これが使用されるべきです。( spi_execp の値リスト 用には使用されるべきではありません。)次のような問い合わせ文字列におい て、Tcl 変数 val が "doesn't" を持つ場合を考えて下さい。

    "SELECT '$val' AS ret"
    
最終的に、次のような問い合わせ文字列になり、spi_exec または spi_prepare 実行中にパースエラーが発生します。
    "SELECT 'doesn't' AS ret"
    
これは
    "SELECT 'doesn''t' AS ret"
    
となるべきですので、次のように記述される必要があります。
    "SELECT '[ quote $val ]' AS ret"
    

spi_exec ?-count n? ?-array name? query ?loop-body? spi_exec ?-count n? ?-array name? 問い合わせ ?loop-body?

Call parser/planner/optimizer/executor for query. The optional -count value tells spi_exec the maximum number of rows to be processed by the query.

問い合わせ用のパーサ/プランナ/オプティマイザ/エクゼキュータを呼び出 します。オプションの -count 値は、spi_exec に問い合わせで処理される 行の最大数を通知します。

If the query is a SELECT statement and the optional loop-body (a body of Tcl commands like in a foreach statement) is given, it is evaluated for each row selected and behaves like expected on continue/break. The values of selected fields are put into variables named as the column names. So a

    spi_exec "SELECT count(*) AS cnt FROM pg_proc"
    
will set the variable $cnt to the number of rows in the pg_proc system catalog. If the option -array is given, the column values are stored in the associative array named 'name' indexed by the column name instead of individual variables.
    spi_exec -array C "SELECT * FROM pg_class" {
        elog DEBUG "have table $C(relname)"
    }
    
will print a DEBUG log message for every row of pg_class. The return value of spi_exec is the number of rows affected by query as found in the global variable SPI_processed.

問い合わせが SELECT 文であり、オプションの loop-body ( foreach 文に似た Tcl コマンドの集合)が指定された場合、選択された行を一つ 一つ評価し、continue/break で期待されるように振舞います。選択され たフィールドの値は、カラム名と同じ名前の変数に代入されます。ですの で、

    spi_exec "SELECT count(*) AS cnt FROM pg_proc"
    
は、pg_proc システムカタログの行数を $cnt 変数に設定します。-array オプションが指定された場合、カラムの値は name という名前の、固有の 変数ではなくカラムの名前でインデックスされた連想配列に保存されます。
    spi_exec -array C "SELECT * FROM pg_class" {
        elog DEBUG "have table $C(relname)"
    }
    
は、pg_class の各行に対して DEBUG レベルのログメッセージを出力し ます。spi_exec の戻り値は、グローバルな SPI_processed 変数のような、 問い合わせによって影響を受けた行の数です。

spi_prepare 問い合わせ 型リスト

Prepares AND SAVES a query plan for later execution. It is a bit different from the C level SPI_prepare in that the plan is automatically copied to the toplevel memory context. Thus, there is currently no way of preparing a plan without saving it.

後の実行用に問い合わせ計画を準備し、保存します。C レベルの SPI_prepare とは少し異なり、その計画は自動的に最上位のメモリコンテ キストにコピーされます。従って、現時点では保存せずに準備を行なうこ とはできません。

If the query references arguments, the type names must be given as a Tcl list. The return value from spi_prepare is a query ID to be used in subsequent calls to spi_execp. See spi_execp for a sample.

問い合わせが引数を参照する場合、その型の名前は Tcl のリストとして与 えられる必要があります。spi_prepare の戻り値は問い合わせ ID であり、 続いて spi_execp を呼び出す時に使われます。実例については spi_execp を見て下さい。

spi_exec ?-count n? ?-array name? ?-nulls str? 問い合わせ ?値リスト? ?loop-body?

Execute a prepared plan from spi_prepare with variable substitution. The optional -count value tells spi_execp the maximum number of rows to be processed by the query.

spi_prepare によって準備された計画を変数に置き換えて実行します。 -count オプションの値は spi_execp に問い合わせによって処理される行 の最大数を通知します。

The optional value for -nulls is a string of spaces and 'n' characters telling spi_execp which of the values are NULL's. If given, it must have exactly the length of the number of values.

-nulls オプションの値は空白と 'n' 文字による文字列で、spi_execp にどの値が NULL かを通知します。指定された場合、正確に値の数の分 の文字列長である必要があります。

The queryid is the ID returned by the spi_prepare call.

queryid は spi_prepare 呼び出しの戻り値であるIDです。 ※上のterm内のqueryはqueryidの誤植と思われる

If there was a typelist given to spi_prepare, a Tcl list of values of exactly the same length must be given to spi_execp after the query. If the type list on spi_prepare was empty, this argument must be omitted.

spi_prepare に型リストが与えられていた場合、そのリストの大きさと 全く同じ大きさの値リストを query の後ろに spi_execp に与えなけれ ばいけません。spi_prepare の型リストが空ならば、この引数は省略さ れなければいけません。

If the query is a SELECT statement, the same as described for spi_exec happens for the loop-body and the variables for the fields selected.

問い合わせが SELECT 文の場合、loop-body と選択されたフィールドの変数 については、spi_exec で示したことと同じことが起こります。

Here's an example for a PL/Tcl function using a prepared plan:

準備された計画を使用した PL/Tcl 関数の例をここに示します。

    CREATE FUNCTION t1_count(int4, int4) RETURNS int4 AS '
        if {![ info exists GD(plan) ]} {
            # prepare the saved plan on the first call
            set GD(plan) [ spi_prepare \\
                    "SELECT count(*) AS cnt FROM t1 WHERE num >= \\$1 AND num <= \\$2" \\
                    int4 ]
        }
        spi_execp -count 1 $GD(plan) [ list $1 $2 ]
        return $cnt
    ' LANGUAGE 'pltcl';
    
Note that each backslash that Tcl should see must be doubled in the query creating the function, since the main parser processes backslashes too on CREATE FUNCTION. Inside the query string given to spi_prepare should really be dollar signs to mark the parameter positions and to not let $1 be substituted by the value given in the first function call. Tcl コードに現れる各バックスラッシュは、関数内で作成する問い合わせ 中では二重にしなければいけないことに注意して下さい。CREATE FUNCTION においてもバックスラッシュを主パーサが処理をするからです。 spi_prepare に渡す問い合わせ文字列の内側では、ドル記号をパラメー タの位置を指定するために使用すべきであり、最初の関数呼び出しで与え られる値で $1 が置換されないようにすべきです。

モジュールと不明なコマンド

PL/Tcl has a special support for things often used. It recognizes two magic tables, pltcl_modules and pltcl_modfuncs. If these exist, the module 'unknown' is loaded into the interpreter right after creation. Whenever an unknown Tcl procedure is called, the unknown proc is asked to check if the procedure is defined in one of the modules. If this is true, the module is loaded on demand. To enable this behavior, the PL/Tcl call handler must be compiled with -DPLTCL_UNKNOWN_SUPPORT set.

PL/Tcl は、多く使われる事柄について特別なサポートを行ないます。 pltcl_modules と pltcl_modfuncs という2つの魔法のようなテーブルを 認知します。これらがある場合、'unknown' というモジュールが作成直後 にインタプリタに読み込まれます。不明な Tcl プロシージャが呼び出され ると、その不明なプロシージャがそのモジュール内に定義された物かどう か尋ねられます。真ならば要求に応じてモジュールが読み込まれます。こ の動作を有効にするには、-DPLTCL_UNKNOWN_SUPPORT を付けて PL/Tcl 呼 び出しハンドラをコンパイルしなくてはいけません。

There are support scripts to maintain these tables in the modules subdirectory of the PL/Tcl source including the source for the unknown module that must get installed initially.

これらのテーブルを管理するサポートスクリプトは、初めからインストー ルされている必要がある unknown モジュールのソースと一緒に、PL/Tcl ソースの modules サブディレクトリにあります。