Programando en C - pg_uname

programacion

En este pequeño artículo vamos a ver como instalar en postgreSQL una función programada en C por nosotros.

La posibilidad que tiene PostgreSQL de poder programar nuestras propias funciones en C y usarlas desde nuestra base de datos es una de las muchas características que hacen a esta base de datos tan potente. Una función programada en C podra tener entre otras cosas, acceso a muchas funciones del sistema y a la velocidad de proceso que C nos brinda.

Hay que reconocer que esto no es una tarea apta para principiantes. Se necesitan conocimientos de C y leer un poco de documentación. La documentación sobre la programación de funciones en C para PostgreSQL esta disponible en el Capítulo 34. Extending SQL y en la Sección 34.9. C-Language Functions del manual de PostgreSQL.

Existe un libro magnífico sobre la programación avanzada en C en sistemas Unix, llamado "Advanced Programming in the UNIX Environment, Second Edition (Addison-Wesley Professional Computing Series)" (ISBN-13: 978-0321525949). Totalmente recomendado para los interesados en el tema.

Como ejemplo vamos a crear una función que acceda a la función uname() del sistema operativo. Tambien veremos como acceder a los datos disponibles mediante uname() desde la base de datos.

Lo primero que tenemos que hacer es programar nuestra función. En nuestro caso vamos a crear un función llamada pg_uname() con la que podamos obtener los parametros sysname, nodename, release, version, machine de nuestro sistema operativo Linux.

Creamos un fichero llamado pg_uname.c con el siguiente contenido:


#include "postgres.h"
#include <string.h>
#include "fmgr.h"
#include <sys/utsname.h>

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

PG_FUNCTION_INFO_V1(pg_uname);

Datum
pg_uname(PG_FUNCTION_ARGS)
{
    text *argument = PG_GETARG_TEXT_P(0);
    size_t argumentlen = VARSIZE(argument)-VARHDRSZ;

    text *result = (text *) palloc(256);
    char *option = (char *) palloc(argumentlen+1);
    
    char sysname[] = "sysname";
    char nodename[] = "nodename";
    char release[] = "release";
    char version[] = "version";
    char machine[] = "machine";
    char null[] = "null";

    struct utsname uname_pointer;
    uname(&uname_pointer);

    memcpy(option,VARDATA(argument),argumentlen);
    option[argumentlen] = '\0';

    if (strcmp(option,sysname) == 0){
      SET_VARSIZE(result, strlen(uname_pointer.sysname) + VARHDRSZ);
      memcpy(VARDATA(result),uname_pointer.sysname,strlen(uname_pointer.sysname));
    }
    else if (strcmp(option,nodename) == 0){
      SET_VARSIZE(result, strlen(uname_pointer.nodename) + VARHDRSZ);
      memcpy(VARDATA(result),uname_pointer.nodename,strlen(uname_pointer.nodename));
    }
    else if (strcmp(option,release) == 0){
       SET_VARSIZE(result, strlen(uname_pointer.release) + VARHDRSZ);
       memcpy(VARDATA(result),uname_pointer.release,strlen(uname_pointer.release));
    }
    else if (strcmp(option,version) == 0){
      SET_VARSIZE(result, strlen(uname_pointer.version) + VARHDRSZ);
      memcpy(VARDATA(result),uname_pointer.version,strlen(uname_pointer.version));
 }
    else if (strcmp(option,machine) == 0){
      SET_VARSIZE(result, strlen(uname_pointer.machine) + VARHDRSZ);
      memcpy(VARDATA(result),uname_pointer.machine,strlen(uname_pointer.machine));
    }
    else{
      memcpy(VARDATA(result),null,sizeof(null));
    }

    pfree(option);
    PG_RETURN_TEXT_P(result);
}

A continuación tenemos que compilar la función e instalarla en un directorio donde postgreSQL pueda leerla y cargarla. Para compilarla utilizamos el compilador por defecto en linux, gcc.


root@server:~$ gcc -I /usr/local/include -I /usr/local/include/postgresql/server 
-fpic -cpg_uname.c

root@server:~$ gcc -I /usr/local/include -I /usr/local/include/postgresql/server 
-shared -o pg_uname.so pg_uname.o 

root@server:~$ cp pg_uname.so /usr/local/lib

Una vez que tenemos la función compilada e instalada en nuestro sistema operativo. Utizaremos psql para decirle a PostgreSQL donde encontrarla.


postgres@server:~$ psql 
Welcome to psql 8.3.7, the PostgreSQL interactive terminal.

Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help with psql commands
       \g or terminate with semicolon to execute query
       \q to quit

postgres=# CREATE OR REPLACE FUNCTION pg_uname(text) RETURNS text 
AS '/usr/local/lib/pg_uname.so', 'pg_uname'
LANGUAGE c STRICT;

CREATE FUNCTION

Despues de esto podemos empezar a utilizar la función desde comandos SQL:


postgres=# SELECT pg_uname('nodename');
      pg_uname      
--------------------
 server.ejemplo.com
(1 row)

postgres=# SELECT pg_uname('machine');
 pg_uname 
----------
 x86_64
(1 row)

Podriamos crear una VIEW llamada sysinfo que utilize pg_uname() junto con otras funciones de PostgreSQL para obtener información sobre nuestro sistema. Por ejemplo:


postgres=# CREATE OR REPLACE VIEW sysinfo AS 
SELECT pg_uname('sysname') AS sysname,
pg_uname('nodename') AS nodename,
pg_uname('release') AS kernel_release,
pg_uname('version') AS "kernel_version",
pg_uname('machine') AS machine,
substr("version"(), 11, 7) AS pgversion,
to_char(pg_postmaster_start_time(),'YYYY-MM-DD HH24:MM:SS') AS pg_start,
age(now(),pg_postmaster_start_time()) AS pg_uptime;
CREATE VIEW

postgres=# \x
Expanded display is on.
postgres=# SELECT * from sysinfo ;

-[ RECORD 1 ]----------------------------------
sysname           | Linux
nodename          | server.ejemplo.com
kernel_release    | 2.6.9-67.0.15.ELsmp
kernel_version    | #1 SMP Tue Apr 22 13:58:43 EDT 2008
machine           | x86_64
pgversion         | 8.3.6 
pg_start          | 2009-02-12 07:02:17
pg_uptime         | 2 mons 5 days 13:12:30.059856

Bueno esto es todo en este artículo, espero que os haya ayudado a entender mejor como instalar en PostgreSQL nuestras propias funciones en C

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Que potente Postgres

Primero felicitaciones por la página y con este post me doy cuenta lo poderoso que es Postgres.

Oye podrías escribir un articulo sobre la orientación a objetos de postgres, tratando de poner un ejemplo mejor al encontrado en la documentación (de las ciudades y capitales) y de esta forma poder mostrar y entender mas a fondo la herencia.

gracias.

Kanibalv

Muy buen articulo. Me ha

Muy buen articulo. Me ha servido de mucho ya que nunca habia usando Postgres con C y lo necesitaba.
Llevo de 2-2 en esta pagina. Esta muy buena. Me tocara subir algo para compartir tambien

edwin Quijada
Gracias

busco libro recomendado

Hola, he visto que recomiendas el libro "Advanced Programming in the UNIX Environment" sabes si existe en castellano?

Gracias
Carme

Imagen de rafaelma

Re: busco libro recomendado

"Advanced Programming in the UNIX Environment"

Que yo sepa este libro no se encuentra en español. Este libro junto con el resto de libros de Richard Stevens son los oráculos de la programación en Unix, redes y TCP/IP.

Mas información sobre el autor: http://www.kohala.com/start/

--
Rafael Martinez
Webmaster

Como Hacer fetch

Hola tenia la pregunta de como hago en C para Recorrer un set de tuplas que me devuelva postgres despues de hacer una consulta, es decir si yo hago por ejemplo en SQL una SELECT * tabla; como hacer para trabajar con cada una de las tuplas que me devuelve esa consulta.
Gracias! Muy buen post!