Como se mencionó anteriormente, hay dos clases de tipos en Postgres: tipos base (definidos en un lenguaje de programación) y tipos compuestos (instancias). Los ejemplos en esta sección hasta los de índices de interfaz se pueden encontrar en complex.sql y complex.c. Los ejemplos compuestos están en funcs.sql.
Un tipo definido por el usuario debe tener siempre funciones de entrada y salida. Estas funciones determinan cómo aparece el tipo en las cadenas (para la entrada por el usuario y la salida para el usuario) y cómo se organiza el tipo en memoria. La función de entrada toma una cadena de caracteres delimitada por null como su entrada y devuelve la representación interna (en memoria) del tipo. La función de salida toma la representación interna del tipo y devuelve una cadena de caracteres delimitada por null. Suponga que queremos definir un tipo complejo que representa números complejos. Naturalmente, elegimos representar un complejo en memoria como la siguiente estructura en C:
typedef struct Complex { double x; double y; } Complex;y una cadena de la forma (x, y) como la representación externa de la cadena. Estas funciones normalmente no son difíciles de escribir, especialmente la función de salida. Sin embargo, hay varios puntos a recordar:
Al definir su representación externa (cadena), recuerde que al final debe escribir un parser completo y robusto para esa representación como su función de entrada!
Complex * complex_in(char *str) { double x, y; Complex *result; if (sscanf(str, " ( %lf , %lf )", &x, &y) != 2) { elog(WARN, "complex_in: error in parsing return NULL; } result = (Complex *)palloc(sizeof(Complex)); result->x = x; result->y = y; return (result); }La función de salida puede ser sencillamente:
char * complex_out(Complex *complex) { char *result; if (complex == NULL) return(NULL); result = (char *) palloc(60); sprintf(result, "(%g,%g)", complex->x, complex->y); return(result); }
Debería intentar hacer las funciones de entrada y salida inversas la una a la otra. Si no lo hace, tendrá problemas serios cuando necesite volcar sus datos en un fichero y después leerlos (por ejemplo, en la base de datos de otra persona en otra computadora). Este es un problema particularmente común cuando hay números en punto flotante de por medio.
Para definir el tipo complejo, necesitamos crear las dos funciones definidas por el usuario complex_in y complex_out antes de crear el tipo:
CREATE FUNCTION complex_in(opaque) RETURNS complex AS 'PGROOT/tutorial/obj/complex.so' LANGUAGE 'c'; CREATE FUNCTION complex_out(opaque) RETURNS opaque AS 'PGROOT/tutorial/obj/complex.so' LANGUAGE 'c'; CREATE TYPE complex ( internallength = 16, input = complex_in, output = complex_out );
Como se discutió antes, Postgres soporta totalmente vectores (o arrays) de tipos base. Además, Postgres soporta vectores de tipos definidos por el usuario también. Cuando usted define un tipo, Postgres automáticamente proporciona soporte para vectores de ese tipo. Por razones históricas, el tipo vector tiene el mismo nombre que el tipo definido por el usuario con el carácter subrayado _ antepuesto. Los tipos compuestos no necesitan ninguna función definida sobre ellos, dado que el sistema ya comprende cómo son por dentro.
Los tipos discutidos hasta este punto son todos objetos "pequeños" -- esto es, son menores que 8KB en tamaño.
Nota: 1024 longwords == 8192 bytes. De hecho, el tipo debe ser considerablemente menor que 8192 bytes, dado que las páginas y tuplas de sobrecarga de Postgres deben caber en esta limitación de 8KB también. El valor real que cabe depende de la arquitectura de la máquina.