mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-27 08:39:28 +08:00
From: Jan Wieck <jwieck@debis.com>
A few minutes ago I sent down the PL/Tcl directory to this list. Look at it and reuse anything that might help to build PL/perl. I really hope that PL/perl and PL/Tcl appear in the 6.3 distribution. I'll do whatever I can to make this happen.
This commit is contained in:
parent
243a913766
commit
a71a80b0f2
43
src/pl/tcl/INSTALL
Normal file
43
src/pl/tcl/INSTALL
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
Installation instructions for PL/Tcl
|
||||||
|
|
||||||
|
1. Build the pltcl shared library
|
||||||
|
|
||||||
|
The Makefile for the pltcl shared library assumes the sources
|
||||||
|
for PostgreSQL are in /usr/local/src/postgresql-6.2.1/src. Edit
|
||||||
|
if not.
|
||||||
|
|
||||||
|
The Makefile depends on the tclConfig.sh file that get's installed
|
||||||
|
with Tcl. This should either be in /usr/lib or in /usr/local/lib.
|
||||||
|
If it is in a different place, edit mkMakefile.tcldefs or make a
|
||||||
|
symbolic link to it here.
|
||||||
|
|
||||||
|
Type make and the shared library should get built.
|
||||||
|
|
||||||
|
2. Now create the PL/Tcl language in PostgreSQL
|
||||||
|
|
||||||
|
Since the pg_language system catalog is private to each database,
|
||||||
|
the new language can be created only for individual databases,
|
||||||
|
or in the template1 database. In the latter case, it is
|
||||||
|
automatically available in all newly created databases.
|
||||||
|
|
||||||
|
The commands to create the new language are:
|
||||||
|
|
||||||
|
create function pltcl_call_handler () returns opaque
|
||||||
|
as 'path-to-pltcl-shared-lib'
|
||||||
|
language 'C';
|
||||||
|
|
||||||
|
create trusted procedural language 'pltcl'
|
||||||
|
handler pltcl_call_handler
|
||||||
|
lancompiler 'PL/Tcl';
|
||||||
|
|
||||||
|
The trusted keyword on create procedural language tells PostgreSQL,
|
||||||
|
that all users (not only those with superuser privilege) are
|
||||||
|
permitted to create functions with LANGUAGE 'pltcl'. This is
|
||||||
|
absolutely safe, because there is nothing a normal user can do
|
||||||
|
with PL/Tcl, to get around access restrictions he/she has.
|
||||||
|
|
||||||
|
3. Use PL/Tcl
|
||||||
|
|
||||||
|
Read pltcl_guide.txt to learn how to write functions and
|
||||||
|
trigger procedures in PL/Tcl.
|
||||||
|
|
91
src/pl/tcl/Makefile
Normal file
91
src/pl/tcl/Makefile
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#-------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Makefile
|
||||||
|
# Makefile for the pltcl shared object
|
||||||
|
#
|
||||||
|
# IDENTIFICATION
|
||||||
|
# $Header: /cvsroot/pgsql/src/pl/tcl/Makefile,v 1.1 1998/02/11 14:07:55 scrappy Exp $
|
||||||
|
#
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#
|
||||||
|
# Tell make where the postgresql sources live
|
||||||
|
#
|
||||||
|
SRCDIR= ../../../src
|
||||||
|
include $(SRCDIR)/Makefile.global
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Include definitions from the tclConfig.sh file
|
||||||
|
#
|
||||||
|
include Makefile.tcldefs
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Uncomment the following to force a specific version of the
|
||||||
|
# Tcl shared library to be used.
|
||||||
|
#
|
||||||
|
#TCL_LIB_SPEC=-L/usr/lib -ltcl8.0
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Change following to how shared library that contain
|
||||||
|
# correct references to libtcl must get built on your system.
|
||||||
|
# Since these definitions come from the tclConfig.sh script,
|
||||||
|
# they should work if the shared build of tcl was successful
|
||||||
|
# on this system.
|
||||||
|
#
|
||||||
|
%$(TCL_SHLIB_SUFFIX): %.o
|
||||||
|
$(TCL_SHLIB_LD) -o $@ $< $(TCL_SHLIB_LD_LIBS) $(TCL_LIB_SPEC) $(TCL_LIBS)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Uncomment the following to enable the unknown command lookup
|
||||||
|
# on the first of all calls to the call handler. See the doc
|
||||||
|
# in the modules directory about details.
|
||||||
|
#
|
||||||
|
#CFLAGS+= -DPLTCL_UNKNOWN_SUPPORT
|
||||||
|
|
||||||
|
|
||||||
|
CC = $(TCL_CC)
|
||||||
|
CFLAGS+= -I$(LIBPQDIR) -I$(SRCDIR)/include $(TCL_SHLIB_CFLAGS)
|
||||||
|
|
||||||
|
# For fmgr.h
|
||||||
|
CFLAGS+= -I$(SRCDIR)/backend
|
||||||
|
|
||||||
|
CFLAGS+= $(TCL_DEFS)
|
||||||
|
|
||||||
|
LDADD+= -L$(LIBPQDIR) -lpq
|
||||||
|
|
||||||
|
#
|
||||||
|
# DLOBJS is the dynamically-loaded object file.
|
||||||
|
#
|
||||||
|
DLOBJS= pltcl$(DLSUFFIX)
|
||||||
|
|
||||||
|
INFILES= $(DLOBJS)
|
||||||
|
|
||||||
|
#
|
||||||
|
# plus exports files
|
||||||
|
#
|
||||||
|
ifdef EXPSUFF
|
||||||
|
INFILES+= $(DLOBJS:.o=$(EXPSUFF))
|
||||||
|
endif
|
||||||
|
|
||||||
|
#
|
||||||
|
# Build the shared lib
|
||||||
|
#
|
||||||
|
all: $(INFILES)
|
||||||
|
|
||||||
|
Makefile.tcldefs:
|
||||||
|
./mkMakefile.tcldefs
|
||||||
|
|
||||||
|
#
|
||||||
|
# Clean
|
||||||
|
#
|
||||||
|
clean:
|
||||||
|
rm -f $(INFILES)
|
||||||
|
rm -f Makefile.tcldefs
|
||||||
|
|
||||||
|
install: all
|
||||||
|
$(INSTALL) $(INSTL_LIB_OPTS) $(DLOBJS) $(DESTDIR)$(LIBDIR)/$(DLOBJS)
|
||||||
|
|
30
src/pl/tcl/license.terms
Normal file
30
src/pl/tcl/license.terms
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
|
|
||||||
|
The following terms apply to all files associated with the
|
||||||
|
software unless explicitly disclaimed in individual files.
|
||||||
|
|
||||||
|
The author hereby grants permission to use, copy, modify,
|
||||||
|
distribute, and license this software and its documentation
|
||||||
|
for any purpose, provided that existing copyright notices are
|
||||||
|
retained in all copies and that this notice is included
|
||||||
|
verbatim in any distributions. No written agreement, license,
|
||||||
|
or royalty fee is required for any of the authorized uses.
|
||||||
|
Modifications to this software may be copyrighted by their
|
||||||
|
author and need not follow the licensing terms described
|
||||||
|
here, provided that the new terms are clearly indicated on
|
||||||
|
the first page of each file where they apply.
|
||||||
|
|
||||||
|
IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY
|
||||||
|
PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN
|
||||||
|
IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGE.
|
||||||
|
|
||||||
|
THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY
|
||||||
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON
|
||||||
|
AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO
|
||||||
|
OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
|
||||||
|
ENHANCEMENTS, OR MODIFICATIONS.
|
22
src/pl/tcl/mkMakefile.tcldefs
Executable file
22
src/pl/tcl/mkMakefile.tcldefs
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
if [ -f ./tclConfig.sh ]; then
|
||||||
|
. ./tclConfig.sh
|
||||||
|
else
|
||||||
|
if [ -f /usr/lib/tclConfig.sh ]; then
|
||||||
|
echo "using tclConfig.sh from /usr/lib"
|
||||||
|
. /usr/lib/tclConfig.sh
|
||||||
|
else
|
||||||
|
if [ -f /usr/local/lib/tclConfig.sh ]; then
|
||||||
|
echo "using tclConfig.sh from /usr/local/lib"
|
||||||
|
. /usr/local/lib/tclConfig.sh
|
||||||
|
else
|
||||||
|
echo "tclConfig.sh not found in /usr/lib or /usr/local/lib"
|
||||||
|
echo "I need this file! Please make a symbolic link to this file"
|
||||||
|
echo "and start make again."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
set | grep '^TCL' >Makefile.tcldefs
|
||||||
|
exit 0
|
2159
src/pl/tcl/pltcl.c
Normal file
2159
src/pl/tcl/pltcl.c
Normal file
File diff suppressed because it is too large
Load Diff
410
src/pl/tcl/pltcl_guide.nr
Normal file
410
src/pl/tcl/pltcl_guide.nr
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
.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 1000
|
||||||
|
PL/Tcl
|
||||||
|
A procedural language for the
|
||||||
|
|
||||||
|
PostgreSQL
|
||||||
|
database system
|
||||||
|
.ce 0
|
||||||
|
.sp 5m
|
||||||
|
.fi
|
||||||
|
.in +4
|
||||||
|
PL/Tcl is a dynamic loadable extension for the PostgreSQL database system
|
||||||
|
that enables the Tcl language to be used to create functions and
|
||||||
|
trigger-procedures. It offers most of the capabilities a function
|
||||||
|
writer has in the C language, except for some restrictions.
|
||||||
|
|
||||||
|
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
|
||||||
|
PostgreSQL user ID like in C. Thus, any unprivileged user may be
|
||||||
|
permitted to use this language.
|
||||||
|
|
||||||
|
The other, internal given, restriction is, that Tcl procedures cannot
|
||||||
|
be used to create input-/output-functions for new data types.
|
||||||
|
.bp
|
||||||
|
.ti -4
|
||||||
|
Data type conversions
|
||||||
|
|
||||||
|
PostgreSQL has a rich set of builtin data types. And new data types can
|
||||||
|
be defined. The trick is, that PostgreSQL doesn't really know much about
|
||||||
|
the internals of a data type. It just offers a container for storing the
|
||||||
|
values and knows some functions to call to convert between the external
|
||||||
|
string representation and the internal container format. In addition, it
|
||||||
|
knows 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 PostgreSQL
|
||||||
|
function has a return value and up to 9 arguments. The data types appear
|
||||||
|
in the pg_type system catalog, where we find their type specific regproc's
|
||||||
|
responsible for input-/output-conversion from/to strings.
|
||||||
|
|
||||||
|
A special case are set values, which can appear as arguments to a
|
||||||
|
function. A set value is like a structure containing all the fields
|
||||||
|
of a table as it's elements.
|
||||||
|
|
||||||
|
C functions cannot have sets as return values. So we cannot do this in
|
||||||
|
Tcl either.
|
||||||
|
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
PostgreSQL functions and Tcl procedure names
|
||||||
|
|
||||||
|
In PostgreSQL, 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 PostgreSQL function are different for Tcl too.
|
||||||
|
.bp
|
||||||
|
.ti -4
|
||||||
|
Defining PostgreSQL functions in PL/Tcl
|
||||||
|
|
||||||
|
The following assumes, that the PL/Tcl language is created by the
|
||||||
|
administrator of the database with the language name 'pltcl'. See the
|
||||||
|
installation 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
|
||||||
|
.fi
|
||||||
|
|
||||||
|
When calling this function in a query, the arguments are given as
|
||||||
|
variables $1 ... $n to the procedure body. So a little max function
|
||||||
|
returning 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
|
||||||
|
.fi
|
||||||
|
|
||||||
|
Set arguments are given to the procedure as Tcl arrays. The element names
|
||||||
|
in the array are the field names of the set. If a field in the actual set
|
||||||
|
has the NULL value, it will not appear in the array! The overpaid_2 sample
|
||||||
|
from 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 < $EMP(salary)} {
|
||||||
|
.in +4
|
||||||
|
return 't'
|
||||||
|
.in -4
|
||||||
|
}
|
||||||
|
if {$EMP(age) < 30 && 100000.0 < $EMP(salary)} {
|
||||||
|
.in +4
|
||||||
|
return 't'
|
||||||
|
.in -4
|
||||||
|
}
|
||||||
|
return 'f'
|
||||||
|
.in -4
|
||||||
|
' language 'pltcl';
|
||||||
|
.in -4
|
||||||
|
.fi
|
||||||
|
|
||||||
|
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. To protect 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.
|
||||||
|
.bp
|
||||||
|
.ti -4
|
||||||
|
Defining trigger procedures in PL/Tcl
|
||||||
|
|
||||||
|
Trigger procedures are defined in PostgreSQL as functions without
|
||||||
|
arguments and a return type of opaque. And so are they in the PL/Tcl
|
||||||
|
language.
|
||||||
|
|
||||||
|
The informations from the trigger manager are given to the procedure body
|
||||||
|
in the following variables:
|
||||||
|
|
||||||
|
.in +4
|
||||||
|
.ti -4
|
||||||
|
$TG_name
|
||||||
|
.br
|
||||||
|
The name of the trigger from the CREATE TRIGGER statement
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
$TG_relid
|
||||||
|
.br
|
||||||
|
The Object ID of the table that caused the trigger procedure to be
|
||||||
|
called.
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
$TG_relatts
|
||||||
|
.br
|
||||||
|
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.
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
$TG_when
|
||||||
|
.br
|
||||||
|
The string BEFORE or AFTER, depending on the event of the trigger call.
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
$TG_level
|
||||||
|
.br
|
||||||
|
The string ROW or STATEMENT, depending on the event of the trigger call.
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
$TG_op
|
||||||
|
.br
|
||||||
|
The string INSERT, UPDATE or DELETE, depending on the event of the trigger
|
||||||
|
call.
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
$NEW
|
||||||
|
.br
|
||||||
|
An array containing the values of the new table row on INSERT/UPDATE
|
||||||
|
actions, or empty on DELETE.
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
$OLD
|
||||||
|
.br
|
||||||
|
An array containing the values of the old table row on UPDATE/DELETE
|
||||||
|
actions, or empty on INSERT.
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
$GD
|
||||||
|
.br
|
||||||
|
The global status data array as described in the functions section of this
|
||||||
|
document.
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
$args
|
||||||
|
.br
|
||||||
|
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.
|
||||||
|
.bp
|
||||||
|
.in -4
|
||||||
|
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.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
.nf
|
||||||
|
.in +4
|
||||||
|
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]
|
||||||
|
.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 -4
|
||||||
|
PostgreSQL database access from PL/Tcl
|
||||||
|
|
||||||
|
The following commands are available to access the database from
|
||||||
|
the body of a PL/Tcl procedure:
|
||||||
|
|
||||||
|
.in +4
|
||||||
|
.ti -4
|
||||||
|
elog level msg
|
||||||
|
.br
|
||||||
|
Fire a log message. Possible levels are NOTICE, WARN, ERROR,
|
||||||
|
FATAL, DEBUG and NOIND
|
||||||
|
like for the elog() C function.
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
quote string
|
||||||
|
.br
|
||||||
|
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
|
||||||
|
|
||||||
|
.ti +4
|
||||||
|
select '$val' as ret
|
||||||
|
|
||||||
|
where the Tcl variable actually contains "doesn't". This would result
|
||||||
|
in the final query string
|
||||||
|
|
||||||
|
.ti +4
|
||||||
|
select 'doesn't' as ret
|
||||||
|
|
||||||
|
what's wrong. It should contain
|
||||||
|
|
||||||
|
.ti +4
|
||||||
|
select 'doesn''t'
|
||||||
|
|
||||||
|
and should be written as
|
||||||
|
|
||||||
|
.ti +4
|
||||||
|
select '[quote $val]' as ret
|
||||||
|
|
||||||
|
to work.
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
spi_exec ?-count n? ?-array name? query ?loop-body?
|
||||||
|
.br
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
.ti +2
|
||||||
|
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.
|
||||||
|
|
||||||
|
.in +2
|
||||||
|
.nf
|
||||||
|
spi_exec -array C "select * from pg_class" {
|
||||||
|
elog DEBUG "have table $C(relname)"
|
||||||
|
}
|
||||||
|
.fi
|
||||||
|
.in -2
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
spi_prepare query typelist
|
||||||
|
.br
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.ti -4
|
||||||
|
spi_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 rows
|
||||||
|
to be processed by the query.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Here's an example for a PL/Tcl function using a prepared plan:
|
||||||
|
|
||||||
|
.in +4
|
||||||
|
.nf
|
||||||
|
create 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 -4
|
||||||
|
|
||||||
|
Note that each backslash that Tcl should see must be doubled in
|
||||||
|
the query creating the function, since the PostgreSQL parser processes
|
||||||
|
backslashes too.
|
||||||
|
.bp
|
||||||
|
.ti -4
|
||||||
|
Modules and the unknown command
|
||||||
|
|
||||||
|
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 called to check if the procedure is defined in one
|
||||||
|
of the modules. If this is true, the module is loaded on demand.
|
||||||
|
|
||||||
|
See the documentation in the modules subdirectory for detailed
|
||||||
|
information.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.in -4
|
||||||
|
Now enjoy PL/Tcl.
|
||||||
|
|
||||||
|
jwieck@debis.com (Jan Wieck)
|
Loading…
Reference in New Issue
Block a user