PL/Tcl es un lenguaje procedural para el gestor de bases de datos Postgres que permite el uso de Tcl para la creaci�n de funciones y procedimientos desencadenados por eventos.
Este paquete fue escrito originalmente por Jan Wieck.
PL/Tcl ofrece la mayor�a de las capacidades de que dispone el lenguaje C, excepto algunas restricciones.
Las restricciones buenas son que todo se ejecuta en un buen interprete Tcl. Adem�s del reducido juego de ordenes de Tcl, solo se disponen de unas pocas ordenes para acceder a bases de datos a trav�s de SPI y para enviar mensajes mediante elog(). No hay forma de acceder a las interioridades del proceso de gesti�n de la base de datos, no de obtener acceso al nivel del sistema operativo, bajo los permisos del identificador de usuario de Postgres, como es posible en C. As�, cualquier usuario de bases de datos sin privilegios puede usar este lenguaje.
La otra restricci�n, interna, es que los procedimientos Tcl no pueden usarse para crear funciones de entrada / salida para nuevos tipos de datos.
Los objetos compartidos para el gestor de llamada PL/Tcl se construyen autom�ticamente y se instalan en el directorio de bibliotecas de Postgres, si el soporte de Tcl/Tk ha sido especificado durante la configuraci�n, en el procedimiento de instalaci�n.
En Postgres, un mismo nombre de funci�n puede usarse para diferentes funciones, siempre que el numero de argumentos o sus tipos sean distintos. Esto puede ocasionar conflictos con los nombres de procedimientos Tcl. Para ofrecer la misma flexibilidad en PL/Tcl, los nombres de procedimientos Tcl internos contienen el identificador de objeto de la fila de procedimientos pg_proc como parte de sus nombres. As�, diferentes versiones (por el numero de argumentos) de una misma funci�n de Postgres pueden ser diferentes tambi�n para Tcl.
Para crear una funci�n en el lenguaje PL/Tcl, se usa la sintaxis
CREATE FUNCTION funcname argumen) RETURNS returntype AS ' # PL/Tcl function body ' LANGUAGE 'pltcl'; |
CREATE FUNCTION tcl_max (int4, int4) RETURNS int4 AS ' if {$1 > $2} {return $1} return $2 ' LANGUAGE 'pltcl'; |
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'; |
A veces (especialmente cuando se usan las funciones SPI que se describir�n m�s adelante) es �til tener algunos datos globales que se mantengan entre dos llamadas al procedimiento. Todos los procedimientos PL/Tcl ejecutados por un backend comparten el mismo interprete de Tcl. Para ayudar a proteger a los procedimientos PL/Tcl de efectos secundarios, una matriz queda disponible para cada uno de los procedimientos a trav�s de la orden 'upvar'. El nombre global de esa variable es el nombre interno asignado por el procedimiento, y el nombre local es GD.
Los procedimientos desencadenados se definen en Postgres como funciones sin argumento y que devuelven un tipo opaco. Y lo mismo en el lenguaje PL/Tcl.
La informaci�n del gestor de procedimientos desencadenados se pasan al cuerpo del procedimiento en las siguientes variables:
El nombre del procedimiento disparador se toma de la sentencia CREATE TRIGGER.
El ID de objeto de la tabla que provoca el desencadenamiento ha de ser invocado.
Una lista Tcl de los nombres de campos de las tablas, precedida de un elemento de lista vac�o. Esto se hace para que al buscar un nombre de elemento en la lista con la orden de Tcl 'lsearch', se devuelva el mismo numero positivo, comenzando por 1, en el que los campos est�n numerados en el catalogo de sistema 'pg_attribute'.
La cadena BEFORE o AFTER, dependiendo del suceso de la llamada desencadenante.
La cadena ROW o STATEMENT, dependiendo del suceso de la llamada desencadenante.
La cadena INSERT, UPDATE o DELETE, dependiendo del suceso de la llamada desencadenante.
Una matriz que contiene los valores de la fila de la nueva tabla para acciones INSERT/UPDATE, o vac�a para DELETE.
Una matriz que contiene los valores de la fila de la vieja tabla para acciones UPDATE o DELETE, o vac�a para INSERT.
La matriz de datos de estado global, como se describa m�s adelante.
Una lista Tcl de los argumentos del procedimiento como se dan en la sentencia CREATE TRIGGER. Los argumentos son tambi�n accesibles como $1 ... $n en el cuerpo del procedimiento.
EL valor devuelto por un procedimiento desencadenado es una de las cadenas OK o SKIP, o una lista devuelta por la orden Tcl 'array get'. Si el valor devuelto es OK, la operaci�n normal que ha desencadenado el procedimiento (INSERT/UPDATE/DELETE) tendr� lugar. Obviamente, SKIP le dice al gestor de procesos desencadenados que suprima silenciosamente la operaci�n. La lista de 'array get' le dice a PL/Tcl que devuelva una fila modificada al gestor de procedimientos desencadenados que ser� insertada en lugar de la dada en $NEW (solo para INSERT/UPDATE). No hay que decir que todo esto solo tiene sentido cuando el desencadenante es BEFORE y FOR EACH ROW.
Ha aqu� un peque�o ejemplo de procedimiento desencadenado que fuerza a un valor entero de una tabla a seguir la pista del numero de actualizaciones que se han realizado en esa fila. Para cada nueva fila insertada, el valor es inicializado a 0, e incrementada en cada operaci�n de actualizaci�n:
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'); |
Las siguientes ordenes permiten acceder a una base de datos desde el interior de un procedimiento PL/Tcl:
Lanza un mensaje de registro. Los posibles niveles son NOTICE, WARN, ERROR, FATAL, DEBUG y NOIND, como en la funci�n 'elog()' de C.
Duplica todas las apariciones de una comilla o de la barra invertida. Deber�a usarse cuando las variables se usen en la cadena en la cadena de la consulta enviada a 'spi_exec' o 'spi_prepara' (no en la lista de valores de 'spi_execp'). Consideremos una cadena de consulta como esta:
"SELECT '$val' AS ret" |
"SELECT 'doesn't' AS ret" |
"SELECT 'doesn''t' AS ret" |
"SELECT '[ quote $val ]' AS ret" |
Llama al analizador/planificador/optimizador/ejecutos de la consulta. El valor opcional -count la dice a 'spi_exec' el m�ximo numero de filas que han de ser procesadas por la consulta.
Si la consulta es una sentencia SELECT y se incluye el cuerpo del lazo opcional (un cuerpo de sentencias Tcl similar a una sentencia anticipada), se eval�a para cada fila seleccionada, y se comporta como se espera, tras continua/break. Los valores de los campos seleccionados se colocan en nombres de variables, como nombres de columnas. As�,
spi_exec "SELECT count(*) AS cnt FROM pg_proc" |
spi_exec -array C "SELECT * FROM pg_class" { elog DEBUG "have table $C(relname)" } |
Prepara Y GUARDA una consulta para una ejecuci�n posterior. Es un poco distinto del caso de C, ya que en ese caso, la consulta prevista es autom�ticamente copiada en el contexto de memoria de mayor nivel. Por lo tanto, no actualmente ninguna forma de planificar una consulta sin guardarla.
Si la consulta hace referencia a argumentos, los nombres de los tipos han de incluirse, en forma de lista Tcl. El valor devuelto por 'spi_prepare' es el identificador de la consulta que se usar� en las siguientes llamadas a 'spi_execp'. V�ase 'spi_execp' para un ejemplo.
Ejecuta una consulta preparada en 'spi_prepare' con sustituci�n de variables. El valor opcional '-count' le dice a 'spi_execp' el m�ximo numero de filas que se procesar�n en la consulta.
El valor opcional para '-nulls' es una cadena de espacios de longitud "n", que le indica a 'spi_execp' qu� valores son NULL. Si se indica, debe tener exactamente la longitud del numero de valores.
El identificador de la consulta es el identificador devuelto por la llamada a 'spi_prepare'.
Si se pasa una lista de tipos a 'spi_prepare', ha de pasarse una lista Tcl de exactamente la misma longitud a 'spi_execp' despu�s de la consulta. Si la lista de tipos de 'spi_prepare' est� vac�a, este argumento puede omitirse.
Si la consulta es una sentencia SELECT, lo que se ha descrito para 'spi_exec' ocurrir� para el cuerpo del bucle y las variables de los campos seleccionados.
He aqu� un ejemplo de una funci�n PL/Tcl que usa una consulta planificada:
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'; |
PL/Tcl tiene una caracter�stica especial para cosas que suceden raramente. Reconoce dos tablas "m�gicas", 'pltcl_modules' y 'pltcl_modfuncs'. Si existen, el m�dulo 'desconocido' es cargado por el interprete, inmediatamente tras su creaci�n. Cada vez que se invoca un procedimiento Tcl desconocido, el procedimiento 'desconocido' es comprobado, por si el procedimiento en cuesti�n est� definido en uno de esos m�dulos. Si ocurre esto, el m�dulo es cargado cuando sea necesario. Para habilitar este comportamiento, el gestor de llamadas de PL/Tcl ha de ser compilado con la opci�n -DPLTCL_UNKNOWN_SUPPORT habilitado.
Existen scripts de soporte para mantener esas tablas en el subdirectorio de m�dulos del c�digo fuente de PL/Tcl, incluyendo el c�digo fuente del m�dulo 'desconocido', que ha de ser instalado inicialmente.