Las funciones escritas en C se pueden compilar en objetos que se pueden cargar de forma din�mica, y usar para implementar funciones SQL definidas por el usuario. La primera vez que la funci�n definida por el usuario es llamada dentro del backend, el cargador din�mico carga el c�digo objeto de la funci�n en memoria, y enlaza la funci�n con el ejecutable en ejecuci�n de Postgres. La sintaxis SQL para CREATE FUNCTION enlaza la funci�n SQL a la funci�n en c�digo C de una de dos formas. Si la funci�n SQL tiene el mismo nombre que la funci�n en c�digo C se usa la primera forma. El argumento cadena en la cl�usula AS es el nombre de camino (pathname) completo del fichero que contiene el objeto compilado que se puede cargar de forma din�mica. Si el nombre de la funci�n C es diferente del nombre deseado de la funci�n SQL, entonces se usa la segunda forma. En esta forma la cl�usula AS toma dos argumentos cadena, el primero es el nombre del camino completo del fichero objeto que se puede cargar de forma din�mica, y el segundo es el s�mbolo de enlace que el cargador din�mico deber�a buscar. Este s�mbolo de enlace es solo el nombre de funci�n en el c�digo fuente C.
Despu�s de que se use por primera vez, una funci�n de usuario, din�micamente cargada, se retiene en memoria, y futuras llamadas a la funci�n solo incurren en la peque�a sobrecarga de una b�squeda de tabla de s�mbolos. |
La cadena que especifica el fichero objeto (la cadena en la cl�usula AS) deber�a ser el camino completo del fichero de c�digo objeto para la funci�n, unido por comillas simples. Si un s�mbolo de enlace se usa en la cl�usula AS, el s�mbolo de enlace se deber�a unir por comillas simples tambi�n, y deber�a ser exactamente el mismo que el nombre de la funci�n en el c�digo fuente C. En sistemas Unix la orden nm imprimir� todos los s�mbolos de enlace de un objeto que se puede cargar de forma din�mica. (Postgres no compilar� una funci�n autom�ticamente; se debe compilar antes de que se use en una orden CREATE FUNCTION. Ver abajo para informaci�n adicional.)
La tabla siguiente da el tipo C requerido para los par�metros en las funciones C que se cargar�n en Postgres. La columna "Defined In" da el fichero de cabecera real (en el directorio .../src/backend/) en el que el tipo C equivalente se define. Sin embargo, si incluye utils/builtins.h, estos ficheros se incluir�n de forma autom�tica.
Tabla 1. Tipos de C equivalentes para los tipos internos de Postgres
Built-In Type | C Type | Defined In |
---|---|---|
abstime | AbsoluteTime | utils/nabstime.h |
bool | bool | include/c.h |
box | (BOX *) | utils/geo-decls.h |
bytea | (bytea *) | include/postgres.h |
char | char | N/A |
cid | CID | include/postgres.h |
datetime | (DateTime *) | include/c.h or include/postgres.h |
int2 | int2 | include/postgres.h |
int2vector | (int2vector *) | include/postgres.h |
int4 | int4 | include/postgres.h |
float4 | float32 or (float4 *) | include/c.h or include/postgres.h |
float8 | float64 or (float8 *) | include/c.h or include/postgres.h |
lseg | (LSEG *) | include/geo-decls.h |
name | (Name) | include/postgres.h |
oid | oid | include/postgres.h |
oidvector | (oidvector *) | include/postgres.h |
path | (PATH *) | utils/geo-decls.h |
point | (POINT *) | utils/geo-decls.h |
regproc | regproc or REGPROC | include/postgres.h |
reltime | RelativeTime | utils/nabstime.h |
text | (text *) | include/postgres.h |
tid | ItemPointer | storage/itemptr.h |
timespan | (TimeSpan *) | include/c.h or include/postgres.h |
tinterval | TimeInterval | utils/nabstime.h |
uint2 | uint16 | include/c.h |
uint4 | uint32 | include/c.h |
xid | (XID *) | include/postgres.h |
Internamente, Postgres considera un tipo base como un "blob de memoria". Las funciones definidas por el usuario que usted define sobre un tipo en turn definen la forma en que Postgres puede operar sobre �l. Esto es, Postgres solo almacenar� y recuperar� los datos desde disco y solo usar� sus funciones definidas por el usuario para introducir y procesar los datos, as� como para obtener la salida de los datos. Los tipos base pueden tener uno de los tres formatos internos siguientes:
pass by value, fixed-length
pass by reference, fixed-length
pass by reference, variable-length
Los tipos por valor solo pueden tener 1, 2 o 4 bytes de longitud (incluso si su computadora soporta tipos por valor de otros tama�os). Postgres mismo solo pasa los tipos entero por valor. Deber�a tener cuidado al definir sus tipos para que tengan el mismo tama�o (en bytes) en todas las arquitecturas. Por ejemplo, el tipo long es peligroso porque es de 4 bytes en algunas m�quinas y de 8 bytes en otras, mientras que el tipo int es de 4 bytes en la mayor�a de las m�quinas Unix (aunque no en la mayor�a de computadores personales). Una implementaci�n razonable del tipo int4 en las m�quinas Unix podr�a ser:
/* 4-byte integer, passed by value */ typedef int int4; |
En el otro lado, los tipos de longitud fija de cualquier tama�o se pueden pasar por referencia. Por ejemplo, aqu� se presenta una implementaci�n de ejemplo de un tipo de Postgres:
/* 16-byte structure, passed by reference */ typedef struct { double x, y; } Point; |
Solo los punteros a tales tipos se pueden usar a la hora de pasarlos como argumentos de entrada o de retorno en las funciones de Postgres. Finalmente, todos los tipos de longitud variable se deben pasar tambi�n por referencia. Todos los tipos de longitud variable deben comenzar con un campo length de exactamente 4 bytes, y todos los datos que se tengan que almacenar dentro de ese tipo deben estar situados en la memoria inmediatamente a continuaci�n de ese campo length. El campo length es la longitud total de la estructura (es decir, incluye el tama�o del campo length mismo). Podemos definir el tipo texto como sigue:
typedef struct { int4 length; char data[1]; } text; |
Obviamente, el campo data no es suficientemente largo para almacenar todas las cadenas posibles; es imposible declarar tal estructura en C. Al manipular tipos de longitud variable, debemos tener cuidado de reservar la cantidad de memoria correcta y de inicializar el campo length. Por ejemplo, si quisi�ramos almacenar 40 bytes en una estructura text, podr�amos usar un fragmento de c�digo como este:
#include "postgres.h" ... char buffer[40]; /* our source data */ ... text *destination = (text *) palloc(VARHDRSZ + 40); destination->length = VARHDRSZ + 40; memmove(destination->data, buffer, 40); ... |
Ahora que hemos visto todas las estructuras posibles para los tipos base, podemos mostrar algunos ejemplos de funciones reales. Suponga que funcs.c es as�:
#include <string.h> #include "postgres.h" /* By Value */ int add_one(int arg) { return(arg + 1); } /* By Reference, Fixed Length */ Point * makepoint(Point *pointx, Point *pointy ) { Point *new_point = (Point *) palloc(sizeof(Point)); new_point->x = pointx->x; new_point->y = pointy->y; return new_point; } /* By Reference, Variable Length */ text * copytext(text *t) { /* * VARSIZE is the total size of the struct in bytes. */ text *new_t = (text *) palloc(VARSIZE(t)); memset(new_t, 0, VARSIZE(t)); VARSIZE(new_t) = VARSIZE(t); /* * VARDATA is a pointer to the data region of the struct. */ memcpy((void *) VARDATA(new_t), /* destination */ (void *) VARDATA(t), /* source */ VARSIZE(t)-VARHDRSZ); /* how many bytes */ return(new_t); } text * concat_text(text *arg1, text *arg2) { int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ; text *new_text = (text *) palloc(new_text_size); memset((void *) new_text, 0, new_text_size); VARSIZE(new_text) = new_text_size; strncpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1)-VARHDRSZ); strncat(VARDATA(new_text), VARDATA(arg2), VARSIZE(arg2)-VARHDRSZ); return (new_text); } |
On OSF/1 we would type:
CREATE FUNCTION add_one(int4) RETURNS int4 AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'; CREATE FUNCTION makepoint(point, point) RETURNS point AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'; CREATE FUNCTION concat_text(text, text) RETURNS text AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'; CREATE FUNCTION copytext(text) RETURNS text AS 'PGROOT/tutorial/funcs.so' LANGUAGE 'c'; |
En otros sistemas, podr�amos tener que especificar la extensi�n del nombre del fichero como .sl (para indicar que es una librer�a (o biblioteca) compartida).
Los tipos compuestos no tienen un formato fijo como las estructuras de C. Las instancias de un tipo compuesto pueden contener campos null. Adem�s, los tipos compuestos que son parte de una jerarqu�a de herencia pueden tener campos diferentes respecto a otros miembros de la misma jerarqu�a de herencia. Por ello, Postgres proporciona una interfaz procedural para acceder a los campos de los tipos compuestos desde C. Cuando Postgres procesa un conjunto de instancias, cada instancia se pasar� a su funci�n como una estructura opaca de tipo TUPLE. Suponga que queremos escribir una funci�n para responder a la consulta
* SELECT name, c_overpaid(EMP, 1500) AS overpaid FROM EMP WHERE name = 'Bill' or name = 'Sam'; |
#include "postgres.h" #include "executor/executor.h" /* for GetAttributeByName() */ bool c_overpaid(TupleTableSlot *t, /* the current instance of EMP */ int4 limit) { bool isnull = false; int4 salary; salary = (int4) GetAttributeByName(t, "salary", &isnull); if (isnull) return (false); return(salary > limit); } |
GetAttributeByName es la funci�n de sistema de Postgres que devuelve los atributos fuera de la instancia actual. Tiene tres argumentos: el argumento de tipo TUPLE pasado a la funci�n, el nombre del atributo deseado, y un par�metro de retorno que describe si el atributo es null. GetAttributeByName alinear� los datos apropiadamente de forma que usted pueda convertir su valor de retorno al tipo deseado. Por ejemplo, si tiene un atributo name que es del tipo name, la llamada a GetAttributeByName ser�a as�:
char *str; ... str = (char *) GetAttributeByName(t, "name", &isnull) |
La consulta siguiente permite que Postgres conozca a la funci�n c_overpaid:
* CREATE FUNCTION c_overpaid(EMP, int4) RETURNS bool AS 'PGROOT/tutorial/obj/funcs.so' LANGUAGE 'c'; |
Aunque hay formas de construir nuevas instancias o de modificar las instancias existentes desde dentro de una funci�n C, �stas son demasiado complejas para discutirlas en este manual.
Ahora volvemos a la tarea m�s dif�cil de escribir funciones del lenguaje de programaci�n. Aviso: esta secci�n del manual no le har� un programador. Debe tener un gran conocimiento de C (incluyendo el uso de punteros y el administrador de memoria malloc) antes de intentar escribir funciones C para usarlas con Postgres. Aunque ser�a posible cargar funciones escritas en lenguajes distintos a C en Postgres, eso es a menudo dif�cil (cuando es posible hacerlo completamente) porque otros lenguajes, tales como FORTRAN y Pascal a menudo no siguen la misma convenci�n de llamada que C. Esto es, otros lenguajes no pasan argumentos y devuelven valores entre funciones de la misma forma. Por esta raz�n, asumiremos que las funciones de su lenguaje de programaci�n est�n escritas en C.
Las funciones C con tipos base como argumentos se pueden escribir de una forma sencilla. Los equivalentes C de los tipos internos de Postgres son accesibles en un fichero C si PGROOT/src/backend/utils/builtins.h se incluye como un fichero de cabecera. Esto se puede conseguir escribiendo
#include <utils/builtins.h> |
Las reglas b�sicas para construir funciones C son las siguientes:
La mayor�a de los ficheros cabecera (include) para Postgres deber�an estar ya instalados en PGROOT/include (ver Figura 2). Deber�a incluir siempre
-I$PGROOT/include |
-I$PGROOT/src/backend -I$PGROOT/src/backend/include -I$PGROOT/src/backend/port/<PORTNAME> -I$PGROOT/src/backend/obj |
Al reservar memoria, use las rutinas de Postgres palloc y pfree en vez de las rutinas de la librer�a de C correspondientes malloc y free. La memoria reservada por palloc se liberar� autom�ticamente al final de cada transacci�n, previniendo fallos de memoria.
Siempre c�ntrese en los bytes de sus estructuras usando memset o bzero. Varias rutinas (tales como el m�todo de acceso hash, hash join y el algoritmo sort) computan funciones de los bits puros contenidos en su estructura. Incluso si usted inicializa todos los campos de su estructura, puede haber varios bytes de relleno de alineaci�n (agujeros en la estructura) que pueden contener valores incorrectos o basura.
La mayor�a de los tipos internos de Postgres se declaran en postgres.h, por eso es una buena idea incluir siempre ese fichero tambi�n. Incluyendo postgres.h incluir� tambi�n elog.h y palloc.h por usted.
Compilar y cargar su c�digo objeto para que se pueda cargar din�micamente en Postgres siempre requiere flags (o banderas) especiales. Ver Enlazando funciones de carga din�mica para una explicaci�n detallada de c�mo hacerlo para su sistema operativo concreto.