Funções em C no database PostgreSQL

O bacana de ministrar treinamentos sobre o database PostgreSQL é que aprendemos sob demanda. Sob a demanda dos alunos....

Dextra

View posts by Dextra
Somos especialistas em desenvolvimento de software sob medida para negócios digitais. Pioneiros na adoção de metodologias de gestão ágil, combinamos processos de design, UX, novas tecnologias e visão de negócio, desenvolvendo soluções que criam oportunidades para nossos clientes. A Dextra faz parte da Mutant, empresa B2B líder no mercado brasileiro e especialista em Customer Experience para plataformas digitais.
Data de publicação: 07/11/2013


O bacana de ministrar treinamentos sobre o database PostgreSQL é que aprendemos sob demanda. Sob a demanda dos alunos. O verdadeiro professor não é aquele que simplesmente passa seu conhecimento, mas aquele que também instiga o aluno a buscar soluções criativas para seus problemas.
O Carlos Mayer, das Lojas MM – Mercadomóveis de Curitiba, um dos alunos do treinamento de PostgreSQL que ministrei por lá, me escreveu, recentemente, com uma questão no mínimo interessante: ele precisava de dentro de uma function PG processar arquivos no sistema de arquivos do SO. Mas de forma a ter total controle do que está sendo executado.


Uma das caracteristicas de segurança do PostgreSQL é não permitir que se tenha acesso diretamente ao sistema
de arquivos, mas há maneiras de se contornar isso, em casos extremos e específicos onde necessita-se de
total controle, programando no nível mais baixo possível.
Parece coisa de outro mundo, mas obviamente não é uma vez que o PostgreSQL conta com a possibilidade de embarcar
para dentro de sua stack, funções em linguagem C, escritas por você mesmo, meu caro programador.
ATENÇÃO: Programar em C profissionalmente, além de perigoso é altamente aditivo.
Recomenda-se acompanhamento de um profissional experiente. ; )
No exemplo, meramente didático, que mostrarei abaixo aproveitamos para criar uma biblioteca em C,
que podemos colocar várias funções e após compilar esta biblioteca e copiá-la para a pasta correta no Postgres,
apenas necessitamos vincular a função já disponível na library para ser vista pelo PostgreSQL.
Abaixo o arquivo em C, que nada mais é que a biblioteca com as funções:

#include "postgres.h"
#include
#include
#include
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "postmaster/syslogger.h"
#include "storage/fd.h"
#include "utils/builtins.h"
#include "utils/datetime.h"
#include "fmgr.h"
#include "miscadmin.h"
#ifdef PG_MODULE_MAGIC
 PG_MODULE_MAGIC;
#endif
typedef struct
{
        char    *location;
        DIR     *dirdesc;
} directory_fctx;
/*-----------------------
 *  * some helper functions
 *   */
/* Auxiliary Functions should go first */
/*
 *  * check for superuser, bark if not.
 *  */
static void
requireSuperuser(void)
{
	if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("only superuser may access this"))));
}
Datum bytea_size(PG_FUNCTION_ARGS);
Datum hello_world(PG_FUNCTION_ARGS);
Datum vnx_pg_logdir_ls(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(bytea_size);
PG_FUNCTION_INFO_V1(hello_world);
PG_FUNCTION_INFO_V1(vnx_pg_logdir_ls);
Datum bytea_size(PG_FUNCTION_ARGS)
{
     bytea *data = PG_GETARG_BYTEA_P(0);
     unsigned char *ptr = (unsigned char *) VARDATA(data);
     int32 tcount = 0, i;
     // count characters
     for (i = VARSIZE(data) - VARHDRSZ; i; i--) {
         if (!i%1000) CHECK_FOR_INTERRUPTS();
	        unsigned char c = *ptr++; // get the char
         tcount++;
     }
     PG_RETURN_INT32(tcount);
}
Datum hello_word(PG_FUNCTION_ARGS)
{
}
Datum
vnx_pg_logdir_ls(PG_FUNCTION_ARGS)
{
        FuncCallContext *funcctx;
        struct dirent *de;
        directory_fctx *fctx;
        if (!superuser())
                ereport(ERROR,
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 (errmsg("only superuser can list the log directory"))));
        if (strcmp(Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log") != 0)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 (errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'"))));
        if (SRF_IS_FIRSTCALL())
        {
                MemoryContext oldcontext;
                TupleDesc       tupdesc;
                funcctx = SRF_FIRSTCALL_INIT();
                oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
                fctx = palloc(sizeof(directory_fctx));
                tupdesc = CreateTemplateTupleDesc(2, false);
                TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
                                                   TIMESTAMPOID, -1, 0);
                TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename",
                                                   TEXTOID, -1, 0);
                funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
                fctx->location = pstrdup(Log_directory);
                fctx->dirdesc = AllocateDir(fctx->location);
                if (!fctx->dirdesc)
                        ereport(ERROR,
                                        (errcode_for_file_access(),
                                         errmsg("could not read directory "%s": %m",
                                                        fctx->location)));
                funcctx->user_fctx = fctx;
                MemoryContextSwitchTo(oldcontext);
        }
        funcctx = SRF_PERCALL_SETUP();
        fctx = (directory_fctx *) funcctx->user_fctx;
        while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
        {
                char       *values[2];
                HeapTuple       tuple;
                char            timestampbuf[32];
                char       *field[MAXDATEFIELDS];
                char            lowstr[MAXDATELEN + 1];
                int                     dtype;
                int                     nf,
                                        ftype[MAXDATEFIELDS];
                fsec_t          fsec;
                int                     tz = 0;
                struct pg_tm date;
                /*
 		** Default format: postgresql-YYYY-MM-DD_HHMMSS.log
 		**/
                if (strlen(de->d_name) != 32
                        || strncmp(de->d_name, "postgresql-", 11) != 0
                        || de->d_name[21] != '_'
                        || strcmp(de->d_name + 28, ".log") != 0)
                        continue;
                /* extract timestamp portion of filename */
                strcpy(timestampbuf, de->d_name + 11);
                timestampbuf[17] = '';
                /* parse and decode expected timestamp to verify it's OK format */
                if (ParseDateTime(timestampbuf, lowstr, MAXDATELEN, field, ftype, MAXDATEFIELDS, &nf))
                        continue;
                if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz))
                        continue;
                /* Seems the timestamp is OK; prepare and return tuple */
                values[0] = timestampbuf;
                values[1] = palloc(strlen(fctx->location) + strlen(de->d_name) + 2);
                sprintf(values[1], "%s/%s", fctx->location, de->d_name);
                tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
                SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
        }
        FreeDir(fctx->dirdesc);
        SRF_RETURN_DONE(funcctx);
}

 
Abaixo o Makefile, que nos permitirá gerar a biblioteca compilada com o comando ‘make’.

MODULES = myLilLibrary
PG_CONFIG = pg_config
PGXS = $(shell $(PG_CONFIG) --pgxs)
INCLUDEDIR = $(shell $(PG_CONFIG) --includedir-server)
include $(PGXS)
myLilLibrary.so: myLilLibrary.o
	cc -shared -o myLilLibrary.so myLilLibrary.o
myLilLibrary.o: myLilLibrary.c
	cc -o myLilLibrary.o -c myLilLibrary.c $(CFLAGS) -I$(INCLUDEDIR)

E então podemos associar a função ao banco conectado com o seguinte comando

-- Associa a função da biblioteca em C, com o PostgreSQL
-- permitindo que o mesmo execute a função em tempo de execução PL/SQL
--
CREATE FUNCTION vnx_pg_logdir_ls() RETURNS TABLE (T timestamp, nome TEXT)
  AS 'myLilLibrary', 'vnx_pg_logdir_ls'
  LANGUAGE C STRICT;
-- Chama a função
--
SELECT * FROM vnx_pg_logdir_ls();

Vinícius Schmidt é analista/consultor em sistemas, infra, banco de dados e segurança da informação.
Especialista em OpenSource desde 1999, atua com PostgreSQL (dentre outros OSS) desde 2000.
 
Colaboraram: Matheus Oliveira (artigo), partes do exemplo em C por Andreas Pflug da PGDG, criador da contrib/adminpack.
 
 

Dextra

View posts by Dextra
Somos especialistas em desenvolvimento de software sob medida para negócios digitais. Pioneiros na adoção de metodologias de gestão ágil, combinamos processos de design, UX, novas tecnologias e visão de negócio, desenvolvendo soluções que criam oportunidades para nossos clientes. A Dextra faz parte da Mutant, empresa B2B líder no mercado brasileiro e especialista em Customer Experience para plataformas digitais.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

um + cinco =

Posts relacionados

  1. Sobre a Dextra

    Somos especialistas em desenvolvimento de software sob medida para negócios digitais. Pioneiros na adoção de metodologias de gestão ágil, combinamos processos de design, UX, novas tecnologias e visão de negócio, desenvolvendo soluções que criam oportunidades para nossos clientes.

  2. Categorias

Scroll to top