📄 pltcl_guide.nr
字号:
.pl 27.0c.ll 17.0c.po 2.0c.nf.nh.de HD.sp 2m...de FT.sp 2m.tl _PL/Tcl_A PostgreSQL PL_Page %...wh 0 HD.wh -3 FT.sp 5m.ce 1000PL/TclA procedural language for thePostgreSQLdatabase system.ce 0.sp 5m.fi.in +4PL/Tcl is a dynamic loadable extension for the PostgreSQL database systemthat enables the Tcl language to be used to create functions andtrigger-procedures. It offers most of the capabilities a functionwriter has in the C language, except for some restrictions.The good restriction is, that everything is executed in a safeTcl-interpreter. In addition to the limited command set of safe Tcl, onlya few commands are available to access the database over SPI and to raisemessages via elog(). There is no way to access internals of thedatabase backend or gaining OS-level access under the permissions of thePostgreSQL user ID like in C. Thus, any unprivileged user may bepermitted to use this language.The other, internal given, restriction is, that Tcl procedures cannotbe used to create input-/output-functions for new data types..bp.ti -4Data type conversionsPostgreSQL has a rich set of builtin data types. And new data types canbe defined. The trick is, that PostgreSQL doesn't really know much aboutthe internals of a data type. It just offers a container for storing thevalues and knows some functions to call to convert between the externalstring representation and the internal container format. In addition, itknows which functions to call to compare containers or to do some arithmetics on them for sorting, indexing and calculations.Tcl on the other hand stores all values as strings.These two different concepts meet perfectly for what we need. A PostgreSQLfunction has a return value and up to 9 arguments. The data types appearin the pg_type system catalog, where we find their type specific regproc'sresponsible for input-/output-conversion from/to strings.A special case are set values, which can appear as arguments to afunction. A set value is like a structure containing all the fieldsof a table as it's elements. C functions cannot have sets as return values. So we cannot do this inTcl either..ti -4PostgreSQL functions and Tcl procedure namesIn PostgreSQL, one and the same function name can be used fordifferent functions as long as the number of arguments or their typesdiffer. This would collide with Tcl procedure names. To offer the sameflexibility in PL/Tcl, the internal Tcl procedure names contain the objectID of the procedures pg_proc row as part of their name. Thus, differentargtype versions of the same PostgreSQL function are different for Tcl too..bp.ti -4Defining PostgreSQL functions in PL/TclThe following assumes, that the PL/Tcl language is created by theadministrator of the database with the language name 'pltcl'. See theinstallation instructions to do that.To create a function in the PL/Tcl language, use the known syntax:.nf CREATE FUNCTION funcname ([typename [...]]).in +4 RETURNS typename AS '.in +4 PL/Tcl procedure body.in -4 ' LANGUAGE 'pltcl';.in -4.fiWhen calling this function in a query, the arguments are given asvariables $1 ... $n to the procedure body. So a little max functionreturning the higher of two int4 values would be created as:.nf create function max (int4, int4).in +4 returns int4 as '.in +4 if {$1 > $2} {return $1} return $2.in -4 ' language 'pltcl';.in -4.fiSet arguments are given to the procedure as Tcl arrays. The element namesin the array are the field names of the set. If a field in the actual sethas the NULL value, it will not appear in the array! The overpaid_2 samplefrom the CREATE FUNCTION section of the manual would be defined in Tcl as.nf create function overpaid_2 (EMP).in +4 returns bool as '.in +4 if {200000.0 < $1(salary)} {.in +4 return 't'.in -4 } if {$1(age) < 30 && 100000.0 < $1(salary)} {.in +4 return 't'.in -4 } return 'f'.in -4 ' language 'pltcl';.in -4.fiSometimes (especially when using the SPI functions described later) itis useful to have some global status data that is held between twocalls to a procedure. To protect PL/Tcl procedures from side effects,an array is made available to each procedure via the upvarcommand. The global name of this variable is the procedures internalname and the local name is GD..bp.ti -4Defining trigger procedures in PL/TclTrigger procedures are defined in PostgreSQL as functions withoutarguments and a return type of opaque. And so are they in the PL/Tcllanguage.The informations from the trigger manager are given to the procedure bodyin the following variables:.in +4.ti -4$TG_name.brThe name of the trigger from the CREATE TRIGGER statement.ti -4$TG_relid.brThe Object ID of the table that caused the trigger procedure to becalled..ti -4$TG_relatts.brA 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 commandreturns the same positive number starting from 1 as the fields are numberedin the pg_attribute system catalog..ti -4$TG_when.brThe string BEFORE or AFTER, depending on the event of the trigger call..ti -4$TG_level.brThe string ROW or STATEMENT, depending on the event of the trigger call..ti -4$TG_op.brThe string INSERT, UPDATE or DELETE, depending on the event of the trigger call..ti -4$NEW.brAn array containing the values of the new table row on INSERT/UPDATEactions, or empty on DELETE..ti -4$OLD.brAn array containing the values of the old table row on UPDATE/DELETEactions, or empty on INSERT..ti -4$GD.brThe global status data array as described in the functions section of thisdocument..ti -4$args.brA Tcl list of the arguments to the procedure as given in theCREATE TRIGGER statement. The arguments are also accessible as $1 ... $nin the procedure body..bp.in -4The 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 valueis OK, the normal operation (INSERT/UPDATE/DELETE) that fired this triggerwill take place. Obviously, SKIP tells the trigger manager to silentlysuppress the operation. The list from 'array get' tells PL/Tclto return a modified row to the trigger manager that will be inserted insteadof the one given in $NEW (INSERT/UPDATE only). Needless to say that allthis is only meaningful when the trigger is BEFORE and FOR EACH ROW.Here's a little example trigger procedure that forces an integer valuein a table to keep track of the # of updates that are performed on therow. For new row's inserted, the value is initialized to 0 and thenincremented on every update operation:.nf.in +4create 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].ti -1 ' language 'pltcl';create table T1 (key int4, modcnt int4, desc text);create trigger trig_T1_modcount before insert or update on T1 for each row execute procedure trigfunc_modcount('modcnt');.in -4.fi.bp.ti -4PostgreSQL database access from PL/TclThe following commands are available to access the database fromthe body of a PL/Tcl procedure:.in +4.ti -4elog level msg.brFire a log message. Possible levels are NOTICE, WARN, ERROR, FATAL, DEBUG and NOINDlike for the elog() C function..ti -4quote string.brDuplicates all occurences of single quote and backslash characters.It should be used when variables are used in the query string givento spi_exec or spi_prepare (not for the value list on spi_execp). Think about a query string like.ti +4select '$val' as retwhere the Tcl variable actually contains "doesn't". This would resultin the final query string .ti +4select 'doesn't' as retwhat's wrong. It should contain.ti +4select 'doesn''t'and should be written as.ti +4select '[quote $val]' as retto work..ti -4spi_exec ?-count n? ?-array name? query ?loop-body?.brCall parser/planner/optimizer/executor for query. The optional -count value tells spi_exec the maximum number of rowsto be processed by the query.If the query isa SELECT statement and the optional loop-body (a body of Tcl commandslike in a foreach statement) is given, it is evaluated for each row selected and behaves like expected on continue/break. The valuesof selected fields are put into variables named as the column names. So a.ti +2spi_exec "select count(*) as cnt from pg_proc"will set the variable $cnt to the number of rows in the pg_proc systemcatalog. If the option -array is given, the column values are storedin the associative array named 'name' indexed by the column nameinstead of individual variables..in +2.nfspi_exec -array C "select * from pg_class" { elog DEBUG "have table $C(relname)"}.fi.in -2will print a DEBUG log message for every row of pg_class. The return valueof spi_exec is the number of rows affected by query as found inthe global variable SPI_processed..ti -4spi_prepare query typelist.brPrepares AND SAVES a query plan for later execution. It is a bit differentfrom the C level SPI_prepare in that the plan is automatically copied to thetoplevel memory context. Thus, there is currently no way of preparing aplan without saving it.If the query references arguments, the type names must be given as a Tcllist. The return value from spi_prepare is a query ID to be used insubsequent calls to spi_execp. See spi_execp for a sample..ti -4spi_execp ?-count n? ?-array name? ?-nulls str? queryid ?values? ?loop-body?Execute a prepared plan from spi_prepare with variable substitution. The optional -count value tells spi_execp the maximum number of rowsto be processed by the query.The optional value for -nulls is a string of spaces and 'n' characterstelling spi_execp which of the values are NULL's. If given, it musthave exactly the length of the number of values.The queryid is the ID returned by the spi_prepare call.If there was a typelist given to spi_prepare, a Tcl list of values ofexactly the same length must be given to spi_execp after the query. Ifthe type list on spi_prepare was empty, this argument must be omitted.If the query is a SELECT statement, the same as described for spi_exechappens for the loop-body and the variables for the fields selected.Here's an example for a PL/Tcl function using a prepared plan:.in +4.nfcreate table T1 (key int4, val text);create function T1_count(int4) returns int4 as ' if {![info exists GD]} { # prepare the plan on the first call set GD(plan) [spi_prepare \\\\ "select count(*) as cnt from T1 where key = \\\\$1" \\\\ int4] } spi_execp -count 1 $GD(plan) [list $1] return $cnt.ti -1 ' language 'pltcl';.fi.in -4Note that each backslash that Tcl should see must be doubled inthe query creating the function, since the PostgreSQL parser processesbackslashes too..bp.ti -4Modules and the unknown commandPL/Tcl has a special support for things often used. It recognizes twomagic tables, pltcl_modules and pltcl_modfuncs.If these exist, the module 'unknown' is loaded into the interpreterright after creation. Whenever an unknown Tcl procedure is called,the unknown proc is called to check if the procedure is defined in oneof the modules. If this is true, the module is loaded on demand.See the documentation in the modules subdirectory for detailedinformation..in -4Now enjoy PL/Tcl.jwieck@debis.com (Jan Wieck)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -