📄 pltcl.sgml
字号:
<programlisting>CREATE FUNCTION t1_count(integer, integer) RETURNS integer 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" \\ [ list int4 int4 ] ] } spi_execp -count 1 $GD(plan) [ list $1 $2 ] return $cnt' LANGUAGE pltcl;</programlisting> Note that each backslash that Tcl should see must be doubled when we type in the function, since the main parser processes backslashes, too, in <command>CREATE FUNCTION</>. We need backslashes inside the query string given to <function>spi_prepare</> to ensure that the <literal>$<replaceable>n</replaceable></> markers will be passed through to <function>spi_prepare</> as-is, and not replaced by Tcl variable substitution. </para> </listitem> </varlistentry> <varlistentry> <indexterm> <primary>spi_lastoid</primary> </indexterm> <term><function>spi_lastoid</></term> <listitem> <para> Returns the OID of the row inserted by the last <function>spi_exec</> or <function>spi_execp</>, if the command was a single-row <command>INSERT</>. (If not, you get zero.) </para> </listitem> </varlistentry> <varlistentry> <term><function>quote</> <replaceable>string</replaceable></term> <listitem> <para> Duplicates all occurrences of single quote and backslash characters in the given string. This may be used to safely quote strings that are to be inserted into SQL commands given to <function>spi_exec</function> or <function>spi_prepare</function>. For example, think about an SQL command string like<programlisting>"SELECT '$val' AS ret"</programlisting> where the Tcl variable <literal>val</> actually contains <literal>doesn't</literal>. This would result in the final command string<programlisting>SELECT 'doesn't' AS ret</programlisting> which would cause a parse error during <function>spi_exec</function> or <function>spi_prepare</function>. The submitted command should contain<programlisting>SELECT 'doesn''t' AS ret</programlisting> which can be formed in PL/Tcl using<programlisting>"SELECT '[ quote $val ]' AS ret"</programlisting> One advantage of <function>spi_execp</function> is that you don't have to quote parameter values like this, since the parameters are never parsed as part of an SQL command string. </para> </listitem> </varlistentry> <varlistentry> <indexterm> <primary>elog</primary> <secondary>in PL/Tcl</secondary> </indexterm> <term><function>elog</> <replaceable>level</replaceable> <replaceable>msg</replaceable></term> <listitem> <para> Emits a log or error message. Possible levels are <literal>DEBUG</>, <literal>LOG</>, <literal>INFO</>, <literal>NOTICE</>, <literal>WARNING</>, <literal>ERROR</>, and <literal>FATAL</>. Most simply emit the given message just like the <literal>elog</> C function. <literal>ERROR</> raises an error condition: further execution of the function is abandoned, and the current transaction is aborted. <literal>FATAL</> aborts the transaction and causes the current session to shut down. (There is probably no good reason to use this error level in PL/Tcl functions, but it's provided for completeness.) </para> </listitem> </varlistentry> </variablelist> </para> </sect1> <sect1 id="pltcl-trigger"> <title>Trigger Procedures in PL/Tcl</title> <indexterm> <primary>trigger</primary> <secondary>in PL/Tcl</secondary> </indexterm> <para> Trigger procedures can be written in PL/Tcl. <productname>PostgreSQL</productname> requires that a procedure that is to be called as a trigger must be declared as a function with no arguments and a return type of <literal>trigger</>. </para> <para> The information from the trigger manager is passed to the procedure body in the following variables: <variablelist> <varlistentry> <term><varname>$TG_name</varname></term> <listitem> <para> The name of the trigger from the <command>CREATE TRIGGER</command> statement. </para> </listitem> </varlistentry> <varlistentry> <term><varname>$TG_relid</varname></term> <listitem> <para> The object ID of the table that caused the trigger procedure to be invoked. </para> </listitem> </varlistentry> <varlistentry> <term><varname>$TG_relatts</varname></term> <listitem> <para> A Tcl list of the table column names, prefixed with an empty list element. So looking up a column name in the list with <application>Tcl</>'s <function>lsearch</> command returns the element's number starting with 1 for the first column, the same way the columns are customarily numbered in <productname>PostgreSQL</productname>. (Empty list elements also appear in the positions of columns that have been dropped, so that the attribute numbering is correct for columns to their right.) </para> </listitem> </varlistentry> <varlistentry> <term><varname>$TG_when</varname></term> <listitem> <para> The string <literal>BEFORE</> or <literal>AFTER</> depending on the type of trigger call. </para> </listitem> </varlistentry> <varlistentry> <term><varname>$TG_level</varname></term> <listitem> <para> The string <literal>ROW</> or <literal>STATEMENT</> depending on the type of trigger call. </para> </listitem> </varlistentry> <varlistentry> <term><varname>$TG_op</varname></term> <listitem> <para> The string <literal>INSERT</>, <literal>UPDATE</>, or <literal>DELETE</> depending on the type of trigger call. </para> </listitem> </varlistentry> <varlistentry> <term><varname>$NEW</varname></term> <listitem> <para> An associative array containing the values of the new table row for <command>INSERT</> or <command>UPDATE</> actions, or empty for <command>DELETE</>. The array is indexed by column name. Columns that are null will not appear in the array. </para> </listitem> </varlistentry> <varlistentry> <term><varname>$OLD</varname></term> <listitem> <para> An associative array containing the values of the old table row for <command>UPDATE</> or <command>DELETE</> actions, or empty for <command>INSERT</>. The array is indexed by column name. Columns that are null will not appear in the array. </para> </listitem> </varlistentry> <varlistentry> <term><varname>$args</varname></term> <listitem> <para> A Tcl list of the arguments to the procedure as given in the <command>CREATE TRIGGER</command> statement. These arguments are also accessible as <literal>$1</literal> ... <literal>$<replaceable>n</replaceable></literal> in the procedure body. </para> </listitem> </varlistentry> </variablelist> </para> <para> The return value from a trigger procedure can be one of the strings <literal>OK</> or <literal>SKIP</>, or a list as returned by the <literal>array get</> Tcl command. If the return value is <literal>OK</>, the operation (<command>INSERT</>/<command>UPDATE</>/<command>DELETE</>) that fired the trigger will proceed normally. <literal>SKIP</> tells the trigger manager to silently suppress the operation for this row. If a list is returned, it tells PL/Tcl to return a modified row to the trigger manager that will be inserted instead of the one given in <varname>$NEW</>. (This works for <command>INSERT</> and <command>UPDATE</> only.) Needless to say that all this is only meaningful when the trigger is <literal>BEFORE</> and <command>FOR EACH ROW</>; otherwise the return value is ignored. </para> <para> Here's a little example trigger procedure that forces an integer value in a table to keep track of the number of updates that are performed on the row. For new rows inserted, the value is initialized to 0 and then incremented on every update operation.<programlisting>CREATE FUNCTION trigfunc_modcount() RETURNS trigger 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 integer, description text, modcnt integer);CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab FOR EACH ROW EXECUTE PROCEDURE trigfunc_modcount('modcnt');</programlisting> Notice that the trigger procedure itself does not know the column name; that's supplied from the trigger arguments. This lets the trigger procedure be reused with different tables. </para> </sect1> <sect1 id="pltcl-unknown"> <title>Modules and the <function>unknown</> command</title> <para> PL/Tcl has support for autoloading Tcl code when used. It recognizes a special table, <literal>pltcl_modules</>, which is presumed to contain modules of Tcl code. If this table exists, the module <literal>unknown</> is fetched from the table and loaded into the Tcl interpreter immediately after creating the interpreter. </para> <para> While the <literal>unknown</> module could actually contain any initialization script you need, it normally defines a Tcl <function>unknown</> procedure that is invoked whenever Tcl does not recognize an invoked procedure name. <application>PL/Tcl</>'s standard version of this procedure tries to find a module in <literal>pltcl_modules</> that will define the required procedure. If one is found, it is loaded into the interpreter, and then execution is allowed to proceed with the originally attempted procedure call. A secondary table <literal>pltcl_modfuncs</> provides an index of which functions are defined by which modules, so that the lookup is reasonably quick. </para> <para> The <productname>PostgreSQL</productname> distribution includes support scripts to maintain these tables: <command>pltcl_loadmod</>, <command>pltcl_listmod</>, <command>pltcl_delmod</>, as well as source for the standard <literal>unknown</> module in <filename>share/unknown.pltcl</>. This module must be loaded into each database initially to support the autoloading mechanism. </para> <para> The tables <literal>pltcl_modules</> and <literal>pltcl_modfuncs</> must be readable by all, but it is wise to make them owned and writable only by the database administrator. </para> </sect1> <sect1 id="pltcl-procnames"> <title>Tcl Procedure Names</title> <para> In <productname>PostgreSQL</productname>, one and the same function name can be used for different functions as long as the number of arguments or their types differ. Tcl, however, requires all procedure names to be distinct. PL/Tcl deals with this by making the internal Tcl procedure names contain the object ID of the function from the system table <structname>pg_proc</> as part of their name. Thus, <productname>PostgreSQL</productname> functions with the same name and different argument types will be different Tcl procedures, too. This is not normally a concern for a PL/Tcl programmer, but it might be visible when debugging. </para> </sect1> </chapter><!-- Keep this comment at the end of the fileLocal variables:mode:sgmlsgml-omittag:nilsgml-shorttag:tsgml-minimize-attributes:nilsgml-always-quote-attributes:tsgml-indent-step:1sgml-indent-data:tsgml-parent-document:nilsgml-default-dtd-file:"./reference.ced"sgml-exposed-tags:nilsgml-local-catalogs:("/usr/lib/sgml/catalog")sgml-local-ecat-files:nilEnd:-->
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -