Un tipo para manejo de moneda Cada objeto moneda guardará tres items de información el nombre de la moneda (euro, dolar, etc) la cantidad de unidades de esa moneda la tasa de cambio en el momento en que el objeto fué creado Implementación en C usa un struct para almacenar una moneda /* File name: fcur.c */ #include "postgres.h" #include "fmgr.h" typedef struct { char fcur_name[4]; /* Currency name */ float4 fcur_units; /* Units of currency */ float4 fcur_xrate; /* Exchange rate */ } fcur; static char * basecurrencyname = "US$"; static char * unknowncurrencyname = "???"; 257
Forma Externa Se desea manejar tres formas units units(exchange-rate) units(exchange-rate/currency-name) Ejemplos 258
Función de Entrada /* Name: fcur_in() Converts an fcur value from external form to internal form. */ PG_FUNCTION_INFO_V1(fcur_in); Datum fcur_in(pg_function_args) { char * src = PG_GETARG_CSTRING(0); char * workstr = pstrdup( src ); char * units = NULL; char * name = NULL; char * xrate = NULL; fcur * result = NULL; char * endptr = NULL; /* strtok() will find all of the components for us */ units = strtok( workstr, "(" ); xrate = strtok( NULL, "/)" ); name = strtok( NULL, ")" ); result = (fcur *)palloc( sizeof( fcur )); memset( result, 0x00, sizeof( fcur )); result->fcur_units = strtod( units, &endptr ); if( xrate ) { result->fcur_xrate = strtod( xrate, &endptr );} else { result->fcur_xrate = 1.0; } if( name ) { strncpy( result->fcur_name, name, sizeof( result->fcur_name ));} else { strncpy( result->fcur_name, unknowncurrencyname, sizeof( result->fcur_name ));} PG_RETURN_POINTER( result ); } 259
Función de Salida /* Name: fcur_out() Converts an fcur value from internal form to external form. */ PG_FUNCTION_INFO_V1(fcur_out); Datum fcur_out(pg_function_args) { fcur * src = (fcur *)PG_GETARG_POINTER( 0 ); char * result; char work[16+sizeof(src->fcur_name)+16+4]; ); } sprintf( work, "%g(%g/%s)", src->fcur_units, src->fcur_xrate, src->fcur_name result = (char *)palloc( strlen( work ) + 1 ); strcpy( result, work ); PG_RETURN_CSTRING( result ); 260
Los toques finales Primero se genera un módulo compartido fcur.so compilando el código C Luego se definen las funciones de entrada y salida en PostgreSQL CREATE OR REPLACE FUNCTION fcur_in( opaque ) RETURNS opaque AS 'fcur.so' LANGUAGE 'C' IMMUTABLE STRICT CREATE OR REPLACE FUNCTION fcur_out( opaque ) RETURNS opaque AS 'fcur.so' LANGUAGE 'C' IMMUTABLE STRICT 261
Utilizando el nuevo tipo CREATE TYPE FCUR ( INPUT=fcur_in, OUTPUT=fcur_out, INTERNALLENGTH=12 ) CREATE TABLE fcur_test( pkey INT, val FCUR ); INSERT INTO fcur_test VALUES( 1, '1' ); INSERT INTO fcur_test VALUES( 2, '1(.5)' ); INSERT INTO fcur_test VALUES( 3, '3(1/US$)' ); INSERT INTO fcur_test VALUES( 4, '5(.687853/GBP)' ); INSERT INTO fcur_test VALUES( 5, '10(7.2566/FRF)' ); INSERT INTO fcur_test VALUES( 6, '1(1.5702/CA$)' ); INSERT INTO fcur_test VALUES( 7, '1.5702(1.5702/CA$)' ); SELECT * FROM fcur_test; pkey val ------+-------------------- 1 1(1/???) 2 1(0.5/???) 3 3(1/US$) 4 5(0.687853/GBP) 5 10(7.2566/FRF) 6 1(1.5702/CA$) 7 1.5702(1.5702/CA$) 262
Mejorando nuestro nuevo tipo Podríamos definir funciones de comparación de monedas SELECT fcur_eq( '1', '1.5702(1.5702/CA$)' ); fcur_eq --------- t (1 row) Podríamos asignar a cada una de estas funciones un operador Es posible asignar operadores de "conmutación" (el inverso) 23 > balance => balance < 23 (permite optimizar consultas) Podríamos definir operadores aritméticos SELECT *, val + '2(1.5702/CA$)' AS result FROM fcur_test; pkey val result ------+--------------------+-------------------- 1 1(1/???) 2.27372(1/US$) 2 1(0.5/???) 3.27372(1/US$) 3 3(1/US$) 4.27372(1/US$) 4 5(0.687853/GBP) 8.54272(1/US$) 5 10(7.2566/FRF) 2.65178(1/US$) 6 1(1.5702/CA$) 3(1.5702/CA$) 7 1.5702(1.5702/CA$) 3.5702(1.5702/CA$) (7 rows) 263
Números Complejos PG_FUNCTION_INFO_V1(complejo_in); Datum complejo_in(pg_function_args) { char *str = PG_GETARG_CSTRING(0); double x, y; Complejo *resultado; typedef struct Complejo { double x; double y; } Complejo; } if (sscanf(str, " ( %lf, %lf )", &x, &y)!= 2) ereport(error, (errcode(errcode_invalid_text_representation), errmsg("syntax invalido para complejo: \"%s\"", str))); resultado = (Complejo *) palloc(sizeof(complejo)); resultado->x = x; resultado->y = y; PG_RETURN_POINTER(resultado); PG_FUNCTION_INFO_V1(complejo_out); Datum complejo_out(pg_function_args) { Complejo *complejo = (Complejo *) PG_GETARG_POINTER(0); char *resultado; } resultado = (char *) palloc(100); snprintf(resultado, 100, "(%g,%g)", complejo->x, complejo->y); PG_RETURN_CSTRING(resultado); 264
Creación del tipo complejo CREATE FUNCTION complejo_in(cstring) RETURNS complejo AS 'filename' LANGUAGE C IMMUTABLE STRICT; CREATE FUNCTION complejo_out(complejo) RETURNS cstring AS 'filename' LANGUAGE C IMMUTABLE STRICT; CREATE TYPE complejo ( internallength = 16, input = complejo_in, output = complejo_out, alignment = double ) ; 265
Operador de suma de complejos CREATE FUNCTION suma_complejo(complejo, complejo) RETURNS complejo AS 'filename', 'suma_complejo' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR + ( leftarg = complejo, rightarg = complejo, procedure = suma_complejo, commutator = + ) ; SELECT (a + b) AS c FROM test_complex; 266