PL/Tcl

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.

Introducci�n

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.

Descripci�n

Funciones de Postgres y nombres de procedimientos Tcl

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.

Definiendo funciones en PL/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';
    
Cuando se invoca esta funci�n en una consulta, los argumentos se dan como variables $1 ... $n en el cuerpo del procedimiento Tcl. As�, una funci�n de m�ximo que devuelva el mayor de dos valores int4 ser�a creada del siguiente modo:
    CREATE FUNCTION tcl_max (int4, int4) RETURNS int4 AS '
        if {$1 > $2} {return $1}
	return $2
    ' LANGUAGE 'pltcl';
    
Argumentos de tipo compuesto se pasan al procedimiento como matrices de Tcl. Los nombres de elementos en la matriz son los nombres de los atributos del tipo compuesto. �Si un atributo de la fila actual tiene el valor NULL, no aparecer� en la matriz! He aqu� un ejemplo que define la funci�n overpaid_2 (que se encuentra en la antigua documentaci�n de Postgres), escrita en 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';
    

Datos Globales en PL/Tcl

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.

Procedimientos desencadenados en PL/Tcl

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:

$TG_name

El nombre del procedimiento disparador se toma de la sentencia CREATE TRIGGER.

$TG_relid

El ID de objeto de la tabla que provoca el desencadenamiento ha de ser invocado.

$TG_relatts

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'.

$TG_when

La cadena BEFORE o AFTER, dependiendo del suceso de la llamada desencadenante.

$TG_level

La cadena ROW o STATEMENT, dependiendo del suceso de la llamada desencadenante.

$TG_op

La cadena INSERT, UPDATE o DELETE, dependiendo del suceso de la llamada desencadenante.

$NEW

Una matriz que contiene los valores de la fila de la nueva tabla para acciones INSERT/UPDATE, o vac�a para DELETE.

$OLD

Una matriz que contiene los valores de la fila de la vieja tabla para acciones UPDATE o DELETE, o vac�a para INSERT.

$GD

La matriz de datos de estado global, como se describa m�s adelante.

$args

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');
    

Acceso a bases de datos desde PL/Tcl

Las siguientes ordenes permiten acceder a una base de datos desde el interior de un procedimiento PL/Tcl:

elog level msg

Lanza un mensaje de registro. Los posibles niveles son NOTICE, WARN, ERROR, FATAL, DEBUG y NOIND, como en la funci�n 'elog()' de C.

quote string

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"
    
Donde la variable Tcl 'val' contiene "doesn't". Esto da lugar a la cadena de consulta
    "SELECT 'doesn't' AS ret"
    
que produce un error del analizador durante la ejecuci�n de 'spi_exec' o 'spi_prepare'. Deber�a contener
    "SELECT 'doesn''t' AS ret"
    
y ha de escribirse de la siguiente manera
    "SELECT '[ quote $val ]' AS ret"
    

spi_exec ?-count n? ?-array nam?que ?loop-body?

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"
    
pondr� en la variable cnt el numero de filas en el cat�logo de sistema 'pg_proc'. Si se incluye la opci�n -array, los valores de columna son almacenados en la matriz asociativa llamada 'name', indexada por el nombre de la columna, en lugar de en variables individuales.
    spi_exec -array C "SELECT * FROM pg_class" {
        elog DEBUG "have table $C(relname)"
    }
    
imprimir� un mensaje de registro DEBUG para cada una de las filas de pg_class. El valor devuelto por spi_exec es el numero de filas afectado por la consulta, y se encuentra en la variable global SPI_processed.

spi_prepare query typelist

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.

spi_exec ?-count n? ?-array nam? ?-nullsesquvalue? ?loop-body?

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';
    
N�tese que cada una de las barras invertidas que Tcl debe ver ha de ser doblada en la consulta que crea la funci�n, dado que el analizador principal procesa estas barras en CREATE FUNCTION. Dentro de la cadena de la consulta que se pasa a 'spi_prepare' debe haber un signo $ para marcar la posici�n del par�metro, y evitar que $1 sea sustituido por el valor dado en la primera llamada a la funci�n.

M�dulos y la orden 'desconocido'

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.