AltaVista
Google

31.9. Funções na linguagem C

As funções definidas pelo usuário podem ser escritas em C (ou numa linguagem que possa ser tornada compatível com a linguagem C, como C++). Estas funções são compiladas em objetos carregáveis dinamicamente (também chamados de bibliotecas compartilhadas), sendo carregadas pelo servidor conforme haja necessidade. A funcionalidade de carregamento dinâmico é o que distingue as funções na "linguagem C" das funções "internas" — as convenções de codificação são essencialmente as mesmas para ambas (portanto, a biblioteca padrão de funções internas é uma preciosa fonte de exemplos de codificação para funções na linguagem C definidas pelo usuário).

Atualmente são utilizadas duas convenções de chamada diferentes para as funções em C. A convenção de chamada mais nova, "versão 1", é indicada pela inclusão da chamada de macro PG_FUNCTION_INFO_V1() na função, conforme mostrado abaixo. A ausência desta macro indica uma função no estilo antigo ("versão 0"). Nestes dois casos o nome da linguagem especificado em CREATE FUNCTION é C. As funções no estilo antigo estão em obsolescência por causa de problemas de portabilidade e ausência de funcionalidades, mas ainda são aceitas por motivo de compatibilidade.

31.9.1. Carregamento dinâmico

Na primeira vez que uma função definida pelo usuário, presente em um arquivo objeto carregável, é chamada em uma sessão, o carregador dinâmico carrega o arquivo objeto na memória para que a função possa ser chamada. O comando CREATE FUNCTION para uma função C definida pelo usuário deve, portanto, especificar duas informações para a função: o nome do arquivo objeto carregável, e o nome C (símbolo de ligação), dentro do arquivo objeto, da função a ser chamada. Se o nome C não for especificado explicitamente, então é assumido como sendo o mesmo nome da função SQL.

É utilizado o seguinte algoritmo para localizar o arquivo objeto compartilhado baseado no nome fornecido no comando CREATE FUNCTION:

  1. Se o nome for um caminho absoluto, o arquivo especificado é carregado.

  2. Se o nome começar pela cadeia de caracteres $libdir, esta parte é substituída pelo diretório de biblioteca do pacote PostgreSQL, determinado em tempo de construção.

  3. Se o nome não contiver a parte do diretório, o arquivo é procurado no caminho especificado pela variável de configuração dynamic_library_path.

  4. Senão (o arquivo não foi encontrado no caminho, ou contém a parte de diretório não-absoluta), o carregador dinâmico tenta usar o nome conforme especificado, o que quase certamente não vai ser bem-sucedido (Não é confiável depender do diretório de trabalho corrente).

Se esta seqüência não for bem-sucedida, a extensão de nome de arquivo de biblioteca compartilhada específica da plataforma (geralmente .so) é anexada ao nome fornecido, e esta seqüência é tentada novamente. Se também não for bem-sucedida, então o carregamento falha.

O ID do usuário sob o qual o PostgreSQL executa deve ser capaz de percorrer o caminho até o arquivo que se deseja carregar. Tornar o arquivo ou um diretório de nível mais alto não legível e/ou não executável pelo usuário postgres é um erro comum.

De qualquer forma, o nome do arquivo fornecido no comando CREATE FUNCTION é gravado literalmente nos catálogos do sistema e, portanto, se for necessário carregar o arquivo novamente o mesmo procedimento é aplicado.

Nota: O PostgreSQL não compila uma função C automaticamente. O arquivo objeto deve ser compilado antes de ser referenciado no comando CREATE FUNCTION. Para obter informações adicionais deve ser consultada a Seção 31.9.6.

O arquivo objeto carregável dinamicamente é mantido na memória após ter sido utilizado pela primeira vez. As chamadas posteriores às funções presentes neste arquivo, na mesma sessão, somente causam um pequeno trabalho extra de procura na tabela de símbolos. Se for necessário obrigar uma nova carga do arquivo objeto, por exemplo após este ser recompilado, deve ser utilizado o comando LOAD, ou iniciada uma nova sessão.

Recomenda-se que as bibliotecas compartilhadas tenham posição relativa a $libdir, ou estejam no caminho de biblioteca dinâmica, simplificando atualizações de versão se a nova instalação estiver em um local diferente. O diretório real representado por $libdir pode ser descoberto através do comando pg_config --pkglibdir. [1]

Antes da versão 7.2 do PostgreSQL, somente era possível especificar no comando CREATE FUNCTION caminhos absolutos exatos para os arquivos objeto. Esta modalidade está em obsolescência, uma vez que torna a definição da função não portável sem necessidade. É melhor especificar apenas o nome da biblioteca compartilhada sem caminho nem extensão, e deixar o mecanismo de procura fornecer estas informações.

31.9.2. Tipos base em funções na linguagem C

Para saber como escrever funções na linguagem C é necessário saber como o PostgreSQL representa internamente os tipos de dado base, e como estes podem ser passados de/para as funções. Internamente, o PostgreSQL considera o tipo base como um "objeto binário grande de memória" (blob of memory). Por sua vez, as funções definidas pelo usuário para o tipo definem a maneira como o PostgreSQL pode operar o tipo, ou seja, o PostgreSQL somente armazena e busca os dados do disco, e usa as funções definidas pelo usuário para entrada, processamento e saída dos dados.

Os tipos base podem ter um destes três formatos internos:

Os tipos passados por valor podem ter comprimento de 1, 2 ou 4 bytes apenas (também de 8 bytes, se na máquina sizeof(Datum) for 8). Deve-se tomar cuidado para que os tipos definidos pelo usuário sejam de tal forma que possuam o mesmo tamanho (em bytes) em todas as arquiteturas. Por exemplo, o tipo long é perigoso, porque possui 4 bytes em algumas máquinas e 8 bytes em outras, enquanto o tipo int possui 4 bytes na maioria das máquinas Unix. Uma implementação razoável do tipo int4 em uma máquina Unix pode ser:

/* inteiro de 4 bytes, passado por valor */
typedef int int4;

Por outro lado, os tipos de comprimento fixo, de qualquer tamanho, podem ser passados por referência. Por exemplo, abaixo está mostrada a implementação de um tipo do PostgreSQL:

/* estrutura de 16 bytes, passada por referência */
typedef struct
{
    double  x, y;
} Point;

Somente podem ser utilizados ponteiros para estes tipos para passá-los de/para as funções do PostgreSQL. Para retornar o valor de um tipo como este, é alocada a quantidade correta de memória com palloc, preenchida a memória alocada, e retornado um ponteiro para a memória alocada (Também pode ser retornado diretamente um valor de entrada, que possua o mesmo tipo do valor retornado, retornando um ponteiro para o valor de entrada. Entretanto, não se deve modificar nunca o conteúdo de um valor passado por referência).

Por fim, todos os tipos de comprimento variável também devem ser passados por referência. Todos os tipos de comprimento variável devem começar por um campo de comprimento, contendo exatamente 4 bytes, e todos os dados a serem armazenados dentro deste tipo devem estar localizados na memória logo após o campo de comprimento. O campo de comprimento contém o comprimento total da estrutura, ou seja, inclui também o tamanho do próprio campo de comprimento.

Como exemplo, o tipo text pode ser definido da seguinte forma:

typedef struct {
    int4 comprimento;
    char dado[1];
} text;

Obviamente, o campo dado declarado não possui comprimento suficiente para armazenar todas as cadeias de caracteres possíveis. Como não é possível declarar estruturas de tamanho variável em C, dependemos do conhecimento de que o compilador C não verifica o intervalo dos índices das matrizes. Simplesmente é alocada a quantidade necessária de espaço, e depois a matriz é acessada como se tivesse sido declarada com o comprimento correto (Este é um truque comum, que pode ser visto em muitos livros texto sobre C).

Ao manipular tipos de comprimento variável, deve-se tomar o cuidado de alocar a quantidade correta de memória, e definir o campo de comprimento corretamente. Por exemplo, se for desejado armazenar 40 bytes em uma estrutura text, pode ser utilizado um fragmento de código como este:

#include "postgres.h"
...
char buffer[40]; /* nosso dado de origem */
...
text *destino = (text *) palloc(VARHDRSZ + 40);
destino->comprimento = VARHDRSZ + 40;
memcpy(destino->dado, buffer, 40);
...

VARHDRSZ é o mesmo que sizeof(int4), mas é considerado um bom estilo utilizar a macro VARHDRSZ para fazer referência ao tamanho adicional para o tipo de comprimento variável.

A Tabela 31-1 especifica qual tipo C corresponde a qual tipo SQL, quando se escreve uma função na linguagem C que utiliza um tipo interno do PostgreSQL. A coluna "Definido em" informa o arquivo de cabeçalho que deve ser incluído para obter a definição do tipo (Na verdade, a definição pode estar em um outro arquivo incluído pelo arquivo informado. Recomenda-se aos usuários que se limitem à interface definida). Deve ser observado que sempre deve ser incluído primeiro, em todos os arquivos fonte, postgres.h, porque este declara várias outras coisas que são sempre necessárias de alguma forma.

Tabela 31-1. Tipos C equivalentes aos tipos SQL internos

Tipo SQL Tipo C Definido em
abstime AbsoluteTime utils/nabstime.h
boolean bool postgres.h (talvez interno do compilador)
box BOX* utils/geo_decls.h
bytea bytea* postgres.h
"char" char (interno do compilador)
character BpChar* postgres.h
cid CommandId postgres.h
date DateADT utils/date.h
smallint (int2) int2 ou int16 postgres.h
int2vector int2vector* postgres.h
integer (int4) int4 ou int32 postgres.h
real (float4) float4* postgres.h
double precision (float8) float8* postgres.h
interval Interval* utils/timestamp.h
lseg LSEG* utils/geo_decls.h
name Name postgres.h
oid Oid postgres.h
oidvector oidvector* postgres.h
path PATH* utils/geo_decls.h
point POINT* utils/geo_decls.h
regproc regproc postgres.h
reltime RelativeTime utils/nabstime.h
text text* postgres.h
tid ItemPointer storage/itemptr.h
time TimeADT utils/date.h
time with time zone TimeTzADT utils/date.h
timestamp Timestamp* utils/timestamp.h
tinterval TimeInterval utils/nabstime.h
varchar VarChar* postgres.h
xid TransactionId postgres.h

Agora que já foram examinadas todas as estruturas possíveis para os tipos base, podem ser mostrados alguns exemplos de funções verdadeiras.

31.9.3. Convenções de chamada Versão-0 para funções na linguagem C

Será apresentado primeiro o "estilo antigo" de convenção de chamada — embora esta modalidade esteja em obsolescência, é mais fácil começar por ela. No método versão-0 os argumentos e o resultado da função C são simplesmente declarados no estilo C usual, mas tomando cuidado para utilizar a representação C de cada tipo de dado SQL conforme mostrado acima.

Abaixo estão mostrados alguns exemplos:

#include "postgres.h"
#include <string.h>

/* Por valor */

int
somar_um(int arg)
{
    return arg + 1;
}

/* Por referência, comprimento fixo */

float8 *
somar_um_float8(float8 *arg)
{
    float8    *resultado = (float8 *) palloc(sizeof(float8));

    *resultado = *arg + 1.0;

    return resultado;
}

Point *
construir_ponto(Point *pontox, Point *pontoy)
{
    Point     *novo_ponto = (Point *) palloc(sizeof(Point));

    novo_ponto->x = pontox->x;
    novo_ponto->y = pontoy->y;

    return novo_ponto;
}

/* Por referência, comprimento variável */

text *
copiar_texto(text *t)
{
    /*
     * VARSIZE é o tamanho total da estrutura em bytes.
     */
    text *novo_t = (text *) palloc(VARSIZE(t));
    VARATT_SIZEP(novo_t) = VARSIZE(t);
    /*
     * VARDATA é o ponteiro para a região de dados da estrutura.
     */
    memcpy((void *) VARDATA(novo_t), /* destino */
           (void *) VARDATA(t),      /* origem */
           VARSIZE(t)-VARHDRSZ);     /* quantidade de bytes */
    return novo_t;
}

text *
concatenar_texto(text *arg1, text *arg2)
{
    int32 tamanho_novo_texto = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
    text *novo_texto = (text *) palloc(tamanho_novo_texto);

    VARATT_SIZEP(novo_texto) = tamanho_novo_texto;
    memcpy(VARDATA(novo_texto), VARDATA(arg1), VARSIZE(arg1)-VARHDRSZ);
    memcpy(VARDATA(novo_texto) + (VARSIZE(arg1)-VARHDRSZ),
           VARDATA(arg2), VARSIZE(arg2)-VARHDRSZ);
    return novo_texto;
}

Supondo que o código acima tenha sido escrito no arquivo funcs.c e compilado dentro de um objeto compartilhado, as funções poderiam ser definidas no PostgreSQL usando comandos como estes:

CREATE FUNCTION somar_um(integer) RETURNS integer
     AS 'DIRETÓRIO/funcs', 'somar_um'
     LANGUAGE C STRICT;

-- observe a sobrecarga do nome de função SQL "somar_um"
CREATE FUNCTION somar_um(double precision) RETURNS double precision
     AS 'DIRETÓRIO/funcs', 'somar_um_float8'
     LANGUAGE C STRICT;

CREATE FUNCTION construir_ponto(point, point) RETURNS point
     AS 'DIRETÓRIO/funcs', 'construir_ponto'
     LANGUAGE C STRICT;

CREATE FUNCTION copiar_texto(text) RETURNS text
     AS 'DIRETÓRIO/funcs', 'copiar_texto'
     LANGUAGE C STRICT;

CREATE FUNCTION concatenar_texto(text, text) RETURNS text
     AS 'DIRETÓRIO/funcs', 'concatenar_texto',
     LANGUAGE C STRICT;

Neste caso, DIRETÓRIO representa o diretório do arquivo de biblioteca compartilhada (por exemplo, o diretório do tutorial do PostgreSQL que contém o código dos exemplos utilizados nesta seção) (Um estilo melhor seria utilizar apenas 'funcs' na cláusula AS, após adicionar DIRETÓRIO ao caminho de procura. Em todo caso, pode ser omitida a extensão específica do sistema para a biblioteca compartilhada, normalmente .so ou .sl).

Deve ser observado que as funções foram especificadas como "strict", significando que o sistema deve assumir, automaticamente, um resultado nulo se algum valor de entrada for nulo. Fazendo assim, evita-se a necessidade de verificar entradas nulas no código da função. Sem isto, seria necessário verificar os valores nulos explicitamente como, por exemplo, verificando a existência de um ponteiro nulo para cada argumento passado por referência (Para os argumentos passados por valor, não haveria nem como verificar!).

Embora esta convenção de chamada seja simples de usar, não é muito portável; em algumas arquiteturas ocorrem problemas ao se passar tipos de dado menores que int desta forma. Também, não existe nenhuma forma simples de retornar um resultado nulo, nem para lidar com argumentos nulos de alguma outra forma que não seja tornando a função estrita. A convenção versão-1, apresentada a seguir, supera estas limitações.

31.9.4. Convenções de chamada Versão-1 para as funções na linguagem C

A convenção de chamada versão-1 se baseia em macros que suprimem a maior parte da complexidade da passagem de argumentos e resultados. A declaração C de uma função versão-1 é sempre

Datum nome_da_função(PG_FUNCTION_ARGS)

Além disso, a chamada de macro

PG_FUNCTION_INFO_V1(nome_da_função);

deve aparecer no mesmo arquivo fonte (por convenção é escrita logo antes da própria função). Esta chamada de macro não é necessária para as funções na linguagem internal, uma vez que o PostgreSQL assume que todas as funções internas usam a convenção versão-1. Entretanto, é requerido nas funções carregadas dinamicamente.

Em uma função versão-1, cada argumento é buscado utilizando a macro PG_GETARG_xxx() correspondente ao tipo de dado do argumento, e o resultado é retornado utilizando a macro PG_RETURN_xxx() para o tipo retornado. A macro PG_GETARG_xxx() recebe como argumento o número do argumento da função a ser buscado, contado a partir de 0. A macro PG_RETURN_xxx() recebe como argumento o valor a ser retornado.

Abaixo estão mostradas as mesmas funções vistas acima, codificadas no estilo versão-1:

#include "postgres.h"
#include <string.h>
#include "fmgr.h"

/* Por valor */

PG_FUNCTION_INFO_V1(somar_um);

Datum
somar_um(PG_FUNCTION_ARGS)
{
    int32   arg = PG_GETARG_INT32(0);

    PG_RETURN_INT32(arg + 1);
}

/* Por referência, comprimento fixo */

PG_FUNCTION_INFO_V1(somar_um_float8);

Datum
somar_um_float8(PG_FUNCTION_ARGS)
{
    /* As macros para FLOAT8 escondem sua natureza de passado por referência. */
    float8   arg = PG_GETARG_FLOAT8(0);

    PG_RETURN_FLOAT8(arg + 1.0);
}

PG_FUNCTION_INFO_V1(construir_ponto);

Datum
construir_ponto(PG_FUNCTION_ARGS)
{
    /* Aqui a natureza de passado por referência de Point não é escondida. */
    Point     *pontox = PG_GETARG_POINT_P(0);
    Point     *pontoy = PG_GETARG_POINT_P(1);
    Point     *novo_ponto = (Point *) palloc(sizeof(Point));

    novo_ponto->x = pontox->x;
    novo_ponto->y = pontoy->y;

    PG_RETURN_POINT_P(novo_ponto);
}

/* Por referência, comprimento variável */

PG_FUNCTION_INFO_V1(copiar_texto);

Datum
copiar_texto(PG_FUNCTION_ARGS)
{
    text     *t = PG_GETARG_TEXT_P(0);
    /*
     * VARSIZE é o tamanho total da estrutura em bytes.
     */
    text     *novo_t = (text *) palloc(VARSIZE(t));
    VARATT_SIZEP(novo_t) = VARSIZE(t);
    /*
     * VARDATA é o ponteiro para a região de dados da estrutura.
     */
    memcpy((void *) VARDATA(novo_t), /* destino */
           (void *) VARDATA(t),      /* origem */
           VARSIZE(t)-VARHDRSZ);     /* quantidade de bytes */
    PG_RETURN_TEXT_P(novo_t);
}

PG_FUNCTION_INFO_V1(concatenar_texto);

Datum
concatenar_texto(PG_FUNCTION_ARGS)
{
    text  *arg1 = PG_GETARG_TEXT_P(0);
    text  *arg2 = PG_GETARG_TEXT_P(1);
    int32 tamanho_novo_texto = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
    text *novo_texto = (text *) palloc(tamanho_novo_texto);

    VARATT_SIZEP(novo_texto) = tamanho_novo_texto;
    memcpy(VARDATA(novo_texto), VARDATA(arg1), VARSIZE(arg1)-VARHDRSZ);
    memcpy(VARDATA(novo_texto) + (VARSIZE(arg1)-VARHDRSZ),
           VARDATA(arg2), VARSIZE(arg2)-VARHDRSZ);
    PG_RETURN_TEXT_P(novo_texto);
}

Os comandos CREATE FUNCTION são idênticos aos das funções versão-0 equivalentes.

À primeira vista, as convenções de codificação versão-1 podem parecer apenas um obscurantismo sem sentido. Entretanto, oferecem várias melhorias porque as macros podem esconder detalhes desnecessários. Como exemplo podemos citar a codificação de somar_um_float8, onde não é mais necessário se preocupar com o fato de float8 ser um tipo passado por referência. Outro exemplo é que as macros GETARG para tipos de comprimento variável permitem buscar valores "toasted" (comprimidos ou fora de linha) de forma mais eficiente.

Uma grande melhoria nas funções versão-1 é o tratamento melhor das entradas e resultados nulos. A macro PG_ARGISNULL(n) permite à função testar se cada um dos valores de entrada é nulo (obviamente, só é necessário nas função não declaradas como "strict"). Nas macros PG_GETARG_xxx() os argumentos de entrada são contados a partir de zero. Deve ser observado que deve ser evitado executar PG_GETARG_xxx() até que tenha sido constatado que o argumento não é nulo. Para retornar um resultado nulo deve ser executado PG_RETURN_NULL(); isto funciona tanto nas funções estritas quanto nas não estritas.

Outra opções fornecidas para a interface no novo estilo são duas variantes das macros PG_GETARG_xxx(). A primeira delas, PG_GETARG_xxx_COPY() garante retornar uma cópia do argumento especificado onde é possível escrever com segurança (As macros comuns, algumas vezes, retornam um ponteiro para um valor que está fisicamente armazenado em uma tabela e, portanto, não é possível escrever neste local. A utilização das macros PG_GETARG_xxx_COPY() garante um resultado onde pode ser escrito).

A segunda variante consiste nas macros PG_GETARG_xxx_SLICE() que recebem três parâmetros. O primeiro é o número do argumento da função (como acima). O segundo e o terceiro são o deslocamento e o comprimento do segmento a ser retornado. Os deslocamentos são contados a partir de zero, e um comprimento negativo requer que o restante do valor seja retornado. Estas macros proporcionam um acesso mais eficiente às partes de valores grandes, caso estes possuam um tipo de armazenamento "external" (O tipo de armazenamento de uma coluna pode ser especificado utilizando ALTER TABLE nome_da_tabela ALTER COLUMN nome_da_coluna SET STORAGE tipo_de_armazenamento. O tipo_de_armazenamento é um entre plain, external, extended ou main).

Por fim, as convenções de chamada de função versão-1 tornam possível retornar "conjuntos" (set) como resultados (Seção 31.9.10), implementar funções de gatilho (Capítulo 32) e tratadores de chamada de linguagem procedural (Capítulo 46). Também, o código versão-1 é mais portável que o código versão-0, porque não quebra as restrições do protocolo de chamada de função padrão do padrão C. Para obter informações adicionais deve ser consultado o arquivo src/backend/utils/fmgr/README na distribuição do código fonte.

31.9.5. Escrita de código

Antes de passar para os tópicos mais avançados, devem ser vistas algumas regras de codificação para funções escritas na linguagem C no PostgreSQL. Embora seja possível carregar funções escritas em outras linguagens diferentes da linguagem C no PostgreSQL, geralmente é difícil (quando não é totalmente impossível) porque as outras linguagens, como C++, FORTRAN e Pascal, geralmente não seguem a mesma convenção de chamada da linguagem C. Ou seja, as outras linguagens não passam argumentos e retornam os valores das funções da mesma maneira. Por este motivo, será assumido que as funções escritas na linguagem C são realmente escritas em C.

As regras básicas para construir funções na linguagem C são as seguintes:

31.9.6. Compilar e ligar as funções carregadas dinamicamente

Antes que as funções escritas em C para estender o PostgreSQL possam ser utilizadas, estas funções precisam ser compiladas e ligadas de uma forma especial para produzir um arquivo que possa ser carregado dinamicamente pelo servidor. Para ser exato, precisa ser criada uma biblioteca compartilhada.

Para obter informações além das contidas nesta seção deve ser lida a documentação do sistema operacional, em particular as páginas do manual do compilador C, gcc, e do editor de ligação, ld. Além disso, o código fonte do PostgreSQL contém vários exemplos funcionais no diretório contrib. Entretanto, a dependência destes exemplos torna os módulos dependentes da disponibilidade do código fonte do PostgreSQL.

Criar bibliotecas compartilhadas geralmente é análogo a ligar executáveis: primeiro os arquivos fonte são compilados em arquivos objetos e, depois, os arquivos objeto são ligados. Os arquivos objeto precisam ser criados como código independente de posição (PIC), o que significa conceitualmente que podem ser colocados em uma posição de memória arbitrária ao serem carregados pelo executável (Os arquivos objeto destinados a executáveis geralmente não são compilados deste modo). O comando para ligar uma biblioteca compartilhada contém sinalizadores especiais para distinguir da ligação de um executável (ao menos em teoria — em alguns sistemas a prática é muito mais feia).

Nos exemplos a seguir é assumido que o código fonte está no arquivo foo.c, e que é criada a biblioteca compartilhada foo.so. O arquivo objeto intermediário se chama foo.o, a não ser que seja dito o contrário. A biblioteca compartilhada pode conter mais de um arquivo objeto, mas aqui é utilizado apenas um arquivo.

BSD/OS

O sinalizador do compilador para criar PIC é -fpic. O sinalizador do ligador para criar biblioteca compartilhada é -shared.

gcc -fpic -c foo.c
ld -shared -o foo.so foo.o

Isto se aplica a partir da versão 4.0 do BSD/OS.

FreeBSD

O sinalizador do compilador para criar PIC é -fpic. Para criar bibliotecas compartilhadas o sinalizador do compilador é -shared.

gcc -fpic -c foo.c
gcc -shared -o foo.so foo.o

Isto se aplica a partir a versão 3.0 do FreeBSD.

HP-UX

O sinalizador do compilador do sistema para criar PIC é +z. Quando se utiliza o GCC é -fpic. O sinalizador do ligador para criar bibliotecas compartilhadas é -b. Portanto

cc +z -c foo.c

ou

gcc -fpic -c foo.c

e depois

ld -b -o foo.sl foo.o

O HP-UX utiliza a extensão .sl para bibliotecas compartilhadas, diferentemente da maioria dos outros sistemas.

IRIX

PIC é o padrão, não é necessário nenhum sinalizador especial do compilador. A opção do ligador para criar bibliotecas compartilhadas é -shared.

cc -c foo.c
ld -shared -o foo.so foo.o
Linux

O sinalizador do compilador para criar PIC é -fpic. Em certas situações em algumas plataformas deve ser utilizado -fPIC se -fpic não funcionar. Deve ser consultado o manual do GCC para obter mais informações. O sinalizador do compilador para criar a biblioteca compartilhada é -shared. Um exemplo completo se parece com:

cc -fpic -c foo.c
cc -shared -o foo.so foo.o
MacOS X

Abaixo segue um exemplo. É assumido que as ferramentas de desenvolvimento estão instaladas.

cc -c foo.c
cc -bundle -flat_namespace -undefined suppress -o foo.so foo.o
NetBSD

O sinalizador do compilador para criar PIC é -fpic. Nos sistemas ELF, é utilizado o compilador com o sinalizador -shared para ligar as bibliotecas compartilhadas. Nos sistemas antigos, não-ELF, é utilizado ld -Bshareable.

gcc -fpic -c foo.c
gcc -shared -o foo.so foo.o
OpenBSD

O sinalizador do compilador para criar PIC é -fpic. É utilizado ld -Bshareable para ligar bibliotecas compartilhadas.

gcc -fpic -c foo.c
ld -Bshareable -o foo.so foo.o
Solaris

O sinalizador do compilador para criar PIC é -KPIC quando é utilizado o compilador da Sun, e -fpic quando é utilizado o GCC. Para ligar bibliotecas compartilhadas a opção do compilador é -G para os dois compiladores ou, como alternativa, -shared quando é utilizado o GCC.

cc -KPIC -c foo.c
cc -G -o foo.so foo.o

ou

gcc -fpic -c foo.c
gcc -G -o foo.so foo.o
Tru64 UNIX

PIC é o padrão e, portanto, o comando para compilar é o usual. É utilizado ld com opções especiais para ligar:

cc -c foo.c
ld -shared -expect_unresolved '*' -o foo.so foo.o

O mesmo procedimento é empregado quando GCC é utilizado no lugar do compilador do sistema; nenhuma opção especial é necessária.

UnixWare

O sinalizador do compilador para criar PIC é -K PIC quando é utilizado o compilador da SCO, e -fpic no GCC. Para ligar bibliotecas compartilhadas a opção do compilador é -G quando é utilizado o compilador da SCO e -shared quando é utilizado o GCC.

cc -K PIC -c foo.c
cc -G -o foo.so foo.o

or

gcc -fpic -c foo.c
gcc -shared -o foo.so foo.o

Dica: Caso julgue muito complicado, poderá ser levado em consideração a utilização da GNU Libtool, que esconde as diferenças entre as plataformas atrás de uma interface uniforme.

O arquivo de biblioteca compartilhada produzido pode então ser carregado no PostgreSQL. Ao se especificar o nome do arquivo no comando CREATE FUNCTION, deve ser especificado o nome do arquivo da biblioteca compartilhada, e não do arquivo objeto intermediário. Deve ser observado que a extensão padrão do sistema para biblioteca compartilhada (geralmente .so ou .sl) pode ser omitida no comando CREATE FUNCTION, e normalmente deve ser omitida para uma melhor portabilidade.

Veja na Seção 31.9.1 onde o servidor espera encontrar os arquivos de biblioteca compartilhada.

31.9.7. Infraestrutura de construção de extensão

Caso se esteja pensando em distribuir os módulos de extensão do PostgreSQL, a implantação de um sistema de construção portável para estes módulos pode ser bem difícil. Por isso, a instalação do PostgreSQL disponibiliza uma infraestrutura de construção de extensões, chamada PGXS, para que módulos de extensão simples possam ser construídos em um servidor já instalado. Deve ser observado que esta infraestrutura não tem por objetivo ser uma estrutura de construção de sistemas universal que possa ser utilizada para construir todos os sistemas que possuem interface com o PostgreSQL; esta infraestrutura simplesmente automatiza as regras comuns de construção para módulos de extensão do servidor simples. Para desenvolver pacotes mais complicados, é necessário escrever seu próprio sistema de construção.

Para utilizar esta infraestrutura para as próprias extensões, deve ser escrito um arquivo de construção, onde é necessário definir algumas variáveis e por fim incluir o arquivo de construção PGXS global. Abaixo está mostrado um exemplo que constrói um módulo de extensão chamado isbn_issn que consiste em uma biblioteca compartilhada, um script SQL, e um arquivo texto de documentação:

MODULES = isbn_issn
DATA_built = isbn_issn.sql
DOCS = README.isbn_issn

PGXS := $(shell pg_config --pgxs)
include $(PGXS)

As duas últimas linhas devem ser sempre as mesmas. Antes disso no arquivo podem ser definidas variáveis e adicionadas regras personalizadas para o make.

Podem ser definidas as seguintes variáveis:

MODULES

lista dos objetos compartilhados a serem construídos a partir do arquivo fonte com o mesmo tronco (não devem ser incluídos sufixos nesta lista)

DATA

arquivos aleatórios a serem instalados em prefix/share/contrib

DATA_built

arquivos aleatórios a serem instalados em prefix/share/contrib, que primeiro devem ser construídos

DOCS

arquivos aleatórios a serem instalados em prefix/doc/contrib

SCRIPTS

arquivos de script (não binários) a serem instalados em prefix/bin

SCRIPTS_built

arquivos de script (não binários) a serem instalados em prefix/bin, que primeiro devem ser construídos

REGRESS

lista de casos de teste de regressão (sem sufixo)

ou no máximo uma destas duas:

PROGRAM

o programa binário a ser construído (lista os arquivos objeto em OBJS)

MODULE_big

o objeto compartilhado a ser construído (lista os arquivos objeto em OBJS)

Também podem ser definidas:

EXTRA_CLEAN

arquivos adicionais a serem removidos por make clean

PG_CPPFLAGS

adicionado a CPPFLAGS

PG_LIBS

adicionado a linha de ligação de PROGRAM

SHLIB_LINK

adicionado a linha de ligação de MODULE_big

Este arquivo de construção deve ser criado com o nome de Makefile no diretório que contém a extensão. Depois pode ser executado make para compilar e, posteriormente, make install para instalar o módulo. A extensão é compilada e instalada na instalação do PostgreSQL que coresponde ao primeiro comando pg_config encontrado no caminho de procura.

31.9.8. Argumentos de tipo composto em funções na linguagem C

Os tipos compostos não possuem uma disposição fixa como as estruturas C. As instâncias de um tipo composto podem conter campos nulos. Além disso, os tipos compostos que são parte de uma hierarquia de herança podem possuir campos diferentes dos outros membros da mesma hierarquia de herança. Por isso, o PostgreSQL disponibiliza uma função de interface para acessar os campos dos tipos compostos a partir da linguagem C.

Suponha que desejamos escrever uma função para responder a consulta

SELECT nome, c_sobrepago(emp, 1500) AS sobrepago
    FROM emp
    WHERE name = 'José' OR name = 'João';

Utilizando as convenções de chamada versão 0, c_sobrepago poderia ser definida como:

#include "postgres.h"
#include "executor/executor.h"  /* para GetAttributeByName() */

bool
c_sobrepago(HeapTupleHeader t, /* a linha corrente de emp */
           int32 limite)
{
    bool isnull;
    int32 salario;

    salario = DatumGetInt32(GetAttributeByName(t, "salario", &isnull));
    if (isnull)
        return false;
    return salario > limite;
}

Na codificação versão-1, o código acima ficaria parecido com:

#include "postgres.h"
#include "executor/executor.h"  /* para GetAttributeByName() */

PG_FUNCTION_INFO_V1(c_sobrepago);

Datum
c_sobrepago(PG_FUNCTION_ARGS)
{
    HeapTupleHeader  t = PG_GETARG_HEAPTUPLEHEADER(0);
    int32            limite = PG_GETARG_INT32(1);
    bool isnull;
    Datum salario;

    salario = GetAttributeByName(t, "salario", &isnull);
    if (isnull)
        PG_RETURN_BOOL(false);
    /* Como alternativa poderia se preferir usar PG_RETURN_NULL() para salário nulo. */

    PG_RETURN_BOOL(DatumGetInt32(salario) > limite);
}

GetAttributeByName é a função de sistema do PostgreSQL que retorna atributos da linha especificada. Possui três argumentos: o argumento do tipo HeapTupleHeader passado para a função, o nome do atributo desejado e o parâmetro retornado que informa se o atributo é nulo. GetAttributeByName retorna um valor do tipo Datum que pode ser convertido no tipo de dado apropriado utilizando a macro DatumGetXXX() apropriada. Deve ser observado que o valor retornado não tem sentido se o sinalizador de nulo estiver definido; sempre deve ser verificado o sinalizador de nulo antes de fazer qualquer coisa com o resultado.

Existe, também, a função GetAttributeByNum que seleciona o atributo de destino pelo número da coluna em vez de pelo nome.

O seguinte comando declara a função c_sobrepago no SQL:

CREATE FUNCTION c_sobrepago(emp, integer) RETURNS boolean
    AS 'DIRETÓRIO/funcs', 'c_sobrepago'
    LANGUAGE C STRICT;

Deve ser observado que foi utilizado STRICT e, portanto, não é necessário verificar se os argumentos são nulos.

31.9.9. Retorno de linhas (tipos compostos) por funções na linguagem C

Para retornar uma linha ou valor de tipo composto por uma função escrita na linguagem C, pode ser utilizada uma API especial que disponibiliza macros e funções que escondem a maior parte da complexidade envolvida na construção de tipos de dado compostos. Para utilizar esta API, deve ser incluído no código fonte:

#include "funcapi.h"

Existem duas maneiras de construir valor de dado composto (de agora em diante "tupla"): pode-se construir a partir de uma matriz de valores Datum, ou a partir de uma matriz de cadeias de caracteres C que possam ser passadas para as funções de conversão de entrada dos tipos de dado das colunas da tupla. Nestes dois casos, primeiro é necessário obter ou construir um descritor TupleDesc para a estrutura da tupla. Quando se trabalha com Datum, é passado o descritor TupleDesc para BlessTupleDesc e depois chamada heap_formtuple para cada linha. Quando se trabalha com cadeias de caracteres C, o descritor TupleDesc é passado para TupleDescGetAttInMetadata e depois chamada BuildTupleFromCStrings para cada linha. No caso de uma função que retorna um conjunto de tuplas, os passos de configuração podem ser todos feitos uma vez durante a primeira chamada à função.

Estão disponíveis várias funções de ajuda para configurar o descritor TupleDesc inicial. Se for desejado utilizar um tipo composto com nome, as informações podem ser buscadas nos catálogos do sistema. Deve ser utilizada

TupleDesc RelationNameGetTupleDesc(const char *relname)

para obter TupleDesc para uma relação pelo nome, ou

TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases)

para obter TupleDesc baseado no OID do tipo. Pode ser utilizado para obter TupleDesc para um tipo base ou composto. Ao se escrever uma função que retorna record, o descritor TupleDesc esperado deve ser passado por quem faz a chamada.

Uma vez que se tenha construído TupleDesc deve ser chamada

TupleDesc BlessTupleDesc(TupleDesc tupdesc)

quando se planeja trabalhar com Datum, ou

AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)

quando se planeja trabalhar com cadeias de caracteres C. Quando se escreve uma função que retorna conjunto, o resultado desta função pode ser salvo na estrutura FuncCallContext — deve ser utilizado o campo tuple_desc ou attinmeta, respectivamente.

Quando se trabalha com Datum deve ser utilizada

HeapTuple heap_formtuple(TupleDesc tupdesc, Datum *values, char *nulls)

para construir HeapTuple a partir dos dados do usuário na forma de Datum.

Quando se trabalha com cadeias de caracteres C deve ser utilizada

HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)

para construir HeapTuple a partir dos dados do usuário na forma de cadeias de caracteres C. values é uma matriz de cadeias de caracteres C, uma para cada atributo da linha retornada. Cada uma das cadeias de caracteres C deve estar na forma esperada pela função de entrada do tipo de dado do atributo. Para ser retornado o valor nulo para um dos atributos o ponteiro correspondente na matriz values deve estar definido como NULL. Esta função precisa ser chamada novamente para cada linha retornada.

Uma vez que se tenha construído a tupla a ser retornada pela função, esta tupla deve ser convertida no tipo Datum. Deve ser utilizada a função

HeapTupleGetDatum(HeapTuple tuple)

para converter HeapTuple em um Datum válido. Este Datum pode ser retornado diretamente se a intenção for retornar apenas uma única linha, ou pode ser utilizado como o valor retornado corrente em uma função que retorna conjunto.

Na próxima seção é mostrado um exemplo.

31.9.10. Retorno de conjunto a partir de funções na linguagem C

Existe, também, uma API especial que fornece suporte a retorno de conjuntos (várias linhas) a partir de função na linguagem C. Uma função que retorna conjunto deve seguir as convenções de chamada versão-1. Além disso, os arquivos fonte devem incluir funcapi.h, conforme mostrado acima.

Uma função que retorna conjunto (SRF) é chamada uma vez para cada item retornado. A SRF deve, portanto, salvar as informações de estado necessárias para se lembrar do que estava fazendo e retornar o próximo item a cada chamada. É fornecida a estrutura FuncCallContext para ajudar a controlar este processo. Dentro da função, é utilizado fcinfo->flinfo->fn_extra para manter um ponteiro para FuncCallContext entre as chamadas.

typedef struct
{
    /*
     * Número de vezes que foi chamada anteriormente
     *
     * call_cntr é inicializada com 0 por SRF_FIRSTCALL_INIT(), e
     * incrementada cada vez que SRF_RETURN_NEXT() é chamada.
     */
    uint32 call_cntr;

    /*
     * OPCIONAL número máximo de chamadas
     *
     * max_calls existe apenas por comodidade, e sua definição é opcional.
     * Se não for definida, deve ser fornecida uma forma alternativa
     * para saber quando a função terminou.
     */
    uint32 max_calls;

    /*
     * OPCIONAL ponteiro para o encaixe (slot) do resultado
     *
     * Isto está obsoleto e somente está presente por motivo de compatibilidade,
     * ou seja, funções que retornam conjunto que utilizam a função em
     * obsolescência TupleDescGetSlot().
     */
    TupleTableSlot *slot;

    /*
     * OPCIONAL ponteiro para informações diversas fornecidas pelo usuário
     *
     * user_fctx é utilizado como ponteiro para os dados do usuário para
     * reter informações arbitrárias de contexto entre chamadas à função.
     */
    void *user_fctx;

    /*
     * OPCIONAL ponteiro para a estrutura contendo metadados do tipo do
     * atributo de entrada
     *
     * attinmeta é utilizado ao se retornar tuplas (ou seja, tipos de dado
     * compostos), não sendo usado para retornar tipos de dado base.
     * Somente é necessário quando há intenção de usar BuildTupleFromCStrings()
     * para criar a tupla a ser retornada.
     */
    AttInMetadata *attinmeta;

    /*
     * contexto de memória utilizado por estruturas que devem permanecer
     * existindo por várias chamadas
     *
     * multi_call_memory_ctx é definido por SRF_FIRSTCALL_INIT() e utilizado
     * por SRF_RETURN_DONE() para limpeza. É o contexto de memória mais
     * apropriado para qualquer memória a ser reutilizada entre várias chamadas
     * à SRF.
     */
    MemoryContext multi_call_memory_ctx;

    /*
     * OPCIONAL ponteiro para a estrutura que contém a descrição da tupla
     *
     * tuple_desc é utilizado ao se retornar tuplas (ou seja, tipos de dado
     * compostos), e somente é necessário se as tuplas forem ser construídas
     * utilizando heap_formtuple() em vez de BuildTupleFromCStrings().
     * Deve ser observado que o ponteiro armazenado aqui geralmente deve
     * ser processado primeiro por BlessTupleDesc().
     */
    TupleDesc tuple_desc;

} FuncCallContext;

Uma SRF utiliza várias funções e macros que manipulam, automaticamente, a estrutura FuncCallContext (e esperam encontrá-la via fn_extra). Deve ser utilizado

SRF_IS_FIRSTCALL()

para determinar se a função está sendo chamada pela primeira vez, ou se está sendo chamada novamente. Na primeira chamada (apenas) deve ser utilizado

SRF_FIRSTCALL_INIT()

para inicializar FuncCallContext. Em todas as chamadas à função, incluindo a primeira, deve ser utilizado

SRF_PERCALL_SETUP()

para configurar de forma apropriada o uso de FuncCallContext, e limpar os dados retornados deixados pela passagem anterior.

Se a função tiver dados a serem retornados, deve ser utilizado

SRF_RETURN_NEXT(funcctx, resultado)

para retornar a quem chamou (resultado deve ser do tipo Datum, tanto para um único valor quanto para uma tupla preparada conforme descrito acima). Por fim, quando a função terminar de retornar os dados, deve ser utilizado

SRF_RETURN_DONE(funcctx)

para limpar e terminar a SRF.

O contexto de memória corrente quando a SRF é chamada é um contexto transiente que é limpo entre as chamadas. Isto significa que não é necessário chamar pfree para tudo que foi alocado usando palloc; vai desaparecer de qualquer forma. Entretanto, se for desejado alocar estruturas de dados que permaneçam existindo entre as chamadas, é necessário colocá-las em outro lugar. O contexto de memória referenciado por multi_call_memory_ctx é um local adequado para todos os dados que precisam continuar existindo até que a SRF termine sua execução. Na maioria dos casos, isto significa que é necessário trocar para multi_call_memory_ctx ao ser feita a configuração na primeira chamada.

Um exemplo de pseudocódigo completo se parece com o seguinte:

Datum
minha_funcao_retornando_conjunto(PG_FUNCTION_ARGS)
{
    FuncCallContext  *funcctx;
    Datum             resultado;
    MemoryContext     contexto_antigo;
    mais declarações que se fizerem necessárias

    if (SRF_IS_FIRSTCALL())
    {
        funcctx = SRF_FIRSTCALL_INIT();
        contexto_antigo = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
        /* O código de configuração de uma única vez aparece aqui: */
        código do usuário
        se retorna tipo composto
            preparar TupleDesc e, talvez, AttInMetadata
        fim se retorna tipo composto
        código do usuário
        MemoryContextSwitchTo(contexto_antigo);
    }

    /* O código de configuração para cada uma das chamadas aparece aqui: */
    código do usuário
    funcctx = SRF_PERCALL_SETUP();
    código do usuário

    /* esta é apenas uma forma possível de testar se terminou: */
    if (funcctx->call_cntr < funcctx->max_calls)
    {
        /* Aqui se deseja retornar outro item: */
        código do usuário
        obter o Datum resultado
        SRF_RETURN_NEXT(funcctx, resultado);
    }
    else
    {
        /* Aqui terminou o retorno de itens e só é necessário fazer a limpeza: */
        código do usuário
        SRF_RETURN_DONE(funcctx);
    }
}

Um exemplo completo de uma SRF simples retornando um tipo composto se parece com:

PG_FUNCTION_INFO_V1(teste_de_passado_por_valor);

Datum
teste_de_passado_por_valor(PG_FUNCTION_ARGS)
{
    FuncCallContext     *funcctx;
    int                  call_cntr;
    int                  max_calls;
    TupleDesc            tupdesc;
    AttInMetadata       *attinmeta;

     /* código executado apenas na primeira chamada a esta função */
     if (SRF_IS_FIRSTCALL())
     {
        MemoryContext   contexto_antigo;

        /* criar o contexto da função para persistência entre chamadas */
        funcctx = SRF_FIRSTCALL_INIT();

        /* mudar para o contexto de memória apropriado para várias chamadas à função */
        contexto_antigo = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);

        /* número total de tuplas a serem retornadas */
        funcctx->max_calls = PG_GETARG_UINT32(0);

        /* construir a descrição de tupla para a tupla __teste_de_passado_por_valor */
        tupdesc = RelationNameGetTupleDesc("__teste_de_passado_por_valor");

        /*
         * gerar metadados de atributos necessários posteriormente
         * para produzir tuplas a partir de cadeias de caractere C
         */
        attinmeta = TupleDescGetAttInMetadata(tupdesc);
        funcctx->attinmeta = attinmeta;

        MemoryContextSwitchTo(contexto_antigo);
    }

    /* código executado em toda chamada a esta função */
    funcctx = SRF_PERCALL_SETUP();

    call_cntr = funcctx->call_cntr;
    max_calls = funcctx->max_calls;
    attinmeta = funcctx->attinmeta;

    if (call_cntr < max_calls)    /* fazer quando há mais a ser enviado */
    {
        char       **values;
        HeapTuple    tuple;
        Datum        resultado;

        /*
         * Preparar a matriz de valores para construir a tupla retornada.
         * Deve ser uma matriz de cadeias de caracteres C a serem processadas
         * posteriormente pelas funções de entrada dos tipos.
         */
        values = (char **) palloc(3 * sizeof(char *));
        values[0] = (char *) palloc(16 * sizeof(char));
        values[1] = (char *) palloc(16 * sizeof(char));
        values[2] = (char *) palloc(16 * sizeof(char));

        snprintf(values[0], 16, "%d", 1 * PG_GETARG_INT32(1));
        snprintf(values[1], 16, "%d", 2 * PG_GETARG_INT32(1));
        snprintf(values[2], 16, "%d", 3 * PG_GETARG_INT32(1));

        /* construir a tupla */
        tuple = BuildTupleFromCStrings(attinmeta, values);

        /* colocar a tupla dentro do datum */
        resultado = HeapTupleGetDatum(tuple);

        /* limpeza (não é realmente necessário) */
        pfree(values[0]);
        pfree(values[1]);
        pfree(values[2]);
        pfree(values);

        SRF_RETURN_NEXT(funcctx, resultado);
    }
    else    /* fazer quando não há mais nada a ser feito */
    {
        SRF_RETURN_DONE(funcctx);
    }
}

O código SQL para declarar esta função é:

CREATE TYPE __teste_de_passado_por_valor AS (f1 integer, f2 integer, f3 integer);

CREATE OR REPLACE FUNCTION teste_de_passado_por_valor(integer, integer)
    RETURNS SETOF __teste_de_passado_por_valor
    AS 'nome_do_arquivo', 'teste_de_passado_por_valor'
    LANGUAGE C IMMUTABLE STRICT;

Na distribuição do código fonte, o diretório contrib/tablefunc contém mais exemplos de funções que retornam conjunto.

31.9.11. Tipos polimórficos em argumentos e retorno

As funções na linguagem C podem ser declaradas como recebendo e retornando os tipos polimórficos anyelement e anyarray. Para obter uma explicação mais detalhada das funções polimórficas deve ser vista Seção 31.2.5. Quando o tipo do argumento da função ou do valor retornado é declarado como polimórfico, o autor da função não pode saber antecipadamente com que tipo de dado a função será chamada, ou usado no retorno. Existem duas rotinas fornecidas em fmgr.h que permitem uma função versão-1 descobrir o tipo de dado verdadeiro de seus argumentos, e o tipo de dado esperado no retorno. As rotinas se chamam get_fn_expr_rettype(FmgrInfo *flinfo) e get_fn_expr_argtype(FmgrInfo *flinfo, int argnum). Retornam o OID do tipo do argumento ou do resultado, ou InvalidOid se a informação não estiver disponível. A estrutura flinfo normalmente é acessada por fcinfo->flinfo. O parâmetro argnum começa por zero.

Por exemplo, suponha que se deseje escrever uma função que aceite um único elemento de qualquer tipo, e retorne uma matriz unidimensional deste tipo:

PG_FUNCTION_INFO_V1(constroi_matriz);
Datum
constroi_matriz(PG_FUNCTION_ARGS)
{
    ArrayType  *resultado;
    Oid         tipo_do_elemento = get_fn_expr_argtype(fcinfo->flinfo, 0);
    Datum       elemento;
    int16       typlen;
    bool        typbyval;
    char        typalign;
    int         ndims;
    int         dims[MAXDIM];
    int         lbs[MAXDIM];

    if (!OidIsValid(tipo_do_elemento))
        elog(ERROR, "não foi possível determinar o tipo de dado de entrada");

    /* obter o elemento fornecido */
    element = PG_GETARG_DATUM(0);

    /* temos uma dimensão */
    ndims = 1;
    /* e um elemento */
    dims[0] = 1;
    /* e o limite inferior é 1 */
    lbs[0] = 1;

    /* obter as informações requeridas sobre o tipo do elemento */
    get_typlenbyvalalign(tipo_do_elemento, &typlen, &typbyval, &typalign);

    /* construir a matriz */
    resultado = construct_md_array(&elemento, ndims, dims, lbs,
                                   tipo_do_elemento, typlen, typbyval, typalign);

    PG_RETURN_ARRAYTYPE_P(resultado);
}

O seguinte comando declara a função constroi_matriz no SQL:

CREATE FUNCTION constroi_matriz(anyelement) RETURNS anyarray
    AS 'DIRETÓRIO/funcs', 'constroi_matriz'
    LANGUAGE C STRICT;

Deve ser observada a utilização de STRICT; isto é essencial uma vez que código não se importa em testar entrada nula.

31.9.12. Exemplos de funções escritas em C

Nota: Esta seção foi escrita pelo tradutor, não fazendo parte do manual original.

Nos exemplos desta seção foram utilizados os arquivos: funcoes.c para as funções codificadas na linguagem C; funcoes.sql para os comandos SQL; e o arquivo Makefile

MODULES = funcoes
DATA = funcoes.sql

PGXS := $(shell pg_config --pgxs)
include $(PGXS)

Todos colocados no diretório ~/funcoes. O código fonte da distribuição do PostgreSQL foi descompactado sob o diretório ~/postgresql-8.0.0 e feita a instalação. Com isto, a geração do arquivo de biblioteca compartilhada foi feita usando os comandos:

-- Gerar a biblioteca compartilhada
cd ~/funcoes
make
gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement \
    -Wold-style-definition -Wendif-labels -fno-strict-aliasing \
    -fpic -I. -I/usr/local/pgsql/include/server \
    -I/usr/local/pgsql/include/internal -D_GNU_SOURCE   \
    -c -o funcoes.o funcoes.c
gcc -shared -o funcoes.so funcoes.o
rm funcoes.o
make install
mkdir -p -- /usr/local/pgsql/share/contrib
/bin/sh /usr/local/pgsql/lib/pgxs/src/makefiles/../../config/install-sh \
        -c -m 644 ./funcoes.sql /usr/local/pgsql/share/contrib
/bin/sh /usr/local/pgsql/lib/pgxs/src/makefiles/../../config/install-sh \
        -c -m 755  funcoes.so /usr/local/pgsql/lib

Exemplo 31-1. Funções C para cálculo de dígitos verificadores- FEBRABAN

Neste exemplo são mostradas duas funções para cálculo do dígito verificador. A primeira função calcula o dígito verificador módulo 11 e a segunda módulo 10. As duas funções são sobrecarregadas (consulte a Seção 31.5): recebem um parâmetro, o número, e retornam o dígito verificador; ou recebem dois parâmetros, o número e o dígito verificador, e retornam verdade ou falso caso o dígito esteja correto ou errado, respectivamente.

O cálculo do dígito verificador módulo 11 é feito da seguinte forma: os dígitos são multiplicados da direita para a esquerda pela seqüência de 2 a 9, ou seja, por 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, e assim por diante; os resultados do produto dos dígitos pelos valores da seqüência são somados; a soma é dividida por 11, e o resto da divisão é obtido; o dígito verificador é calculado subtraindo o resto de 11; quando o resto for igual a 0 ou 1 o dígito verificador será igual a 0. Segundo o Manual Técnico Operacional - Bloquetos de Cobrança - FEBRABAN, deve ser utilizado o dígito 1 quando o resto for 0, 10 ou 1. Existem Secretarias de Fazenda onde no cálculo do dígito verificador a seqüência retorna a 2 antes de chegar a 9. Tome cuidado antes de utilizar esta função.

O cálculo do dígito verificador módulo 10 é feito da seguinte forma: os dígitos são multiplicados da direita para a esquerda pela seqüência 2, 1, 2, 1, e assim por diante; os "algarismos" dos resultados do produto dos dígitos pelos valores da seqüência são somados individualmente; a soma é dividida por 10, e o resto da divisão é obtido; o dígito verificador é calculado subtraindo o resto de 10; quando o resto for igual a 0 o dígito verificador será igual a 0. Fonte: Manual Técnico Operacional - Bloquetos de Cobrança - FEBRABAN. Existe outra forma de calcular este dígito verificador onde os resultados dos produtos, e não os dígitos dos resultados dos produtos, são somados. Tome cuidado antes de utilizar esta função.

Este exemplo mostra o código fonte C da biblioteca compartilhada para cálculo de dígitos verificadores conforme o Manual Técnico Operacional - Bloquetos de Cobrança - FEBRABAN.

/*
 *  Rotina para cálculo do dígito verificador módulo 11.
 *  Recebe dois argumentos, número e dígito verificador na forma de caracteres.
 *  Retorna verdade se o dígito verificador estiver correto, falso caso
 *  contrário, ou nulo em caso de erro.
 */

PG_FUNCTION_INFO_V1(dv11);

Datum
dv11(PG_FUNCTION_ARGS) {
    int  digito = 0,    // Dígito verificador
         fator  = 2;    // Fator de multiplicação
    text *num;          // Primeiro argumento = número
    text *dv;           // Segundo argumento = dígito verificador
    char *c;            // Ponteiro para os caracteres do número
    /* Verificar o recebimento de argumento nulo */
    if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
        PG_RETURN_NULL();
    /* Receber os argumentos */
    num = PG_GETARG_TEXT_P(0);
    dv  = PG_GETARG_TEXT_P(1);
    /* Verificar o recebimento de argumento vazio */
    if ((VARSIZE(num) == VARHDRSZ) || (VARSIZE(dv) == VARHDRSZ)) {
       PG_RETURN_NULL();
    }
    /* Verificar dígito verificador não dígito */
    if (!isdigit(*VARDATA(dv))) PG_RETURN_NULL();
    /* Calcular o dígito verificador */
    for (c = VAREND(num); c >= VARDATA(num); c--) {
        if (!isdigit(*c)) PG_RETURN_NULL(); // Verificar se é dígito
        digito += VALOR(*c) * fator;
        if (++fator > 9) fator = 2;
    }
    digito = 11 - ( digito % 11 );
    if (digito >= 10) digito = 0; // Restos 0 ou 1 dígito = 0
    // Retornar verdade ou falso
    PG_RETURN_BOOL (digito == VALOR(*VARDATA(dv)));
}

/*
 *  Rotina para cálculo do dígito verificador módulo 11.
 *  Recebe um argumento, o número na forma de caracteres.
 *  Retorna o dígito verificador, ou nulo em caso de erro.
 */

PG_FUNCTION_INFO_V1(dv11dig);

Datum
dv11dig(PG_FUNCTION_ARGS) {
    int  digito=0,      // Dígito verificador
         fator=2;       // Fator de multiplicação
    text *num;          // Único argumento = número
    char *c;            // Ponteiro para os caracteres do número
    int32 tamanho;      // Tamanho do resultado da função
    text *resultado;    // Ponteiro para o resultado da função
    /* Verificar o recebimento de argumento nulo */
    if (PG_ARGISNULL(0))
        PG_RETURN_NULL();
    /* Receber o argumento */
    num  = PG_GETARG_TEXT_P(0);
    /* Verificar o recebimento de argumento vazio */
    if (VARSIZE(num) == VARHDRSZ) {
        PG_RETURN_NULL();
    }
    /* Calcular o dígito verificador */
    for (c = VAREND(num); c >= VARDATA(num); c--) {
        if (!isdigit(*c)) PG_RETURN_NULL(); // Verificar se é dígito
        digito += VALOR(*c) * fator;
        if (++fator > 9) fator = 2;
    }
    digito = 11 - ( digito % 11 );
    if (digito >= 10) digito = 0; // Restos 0 ou 1 dígito = 0
    /* Retornar o dígito verificador */
    tamanho = VARHDRSZ + sizeof(char);
    resultado = (text *) palloc(tamanho);
    memset((void *) resultado, 0, tamanho);
    VARATT_SIZEP(resultado) = tamanho;
    *VARDATA(resultado) = (char) DIGITO(digito);
    PG_RETURN_TEXT_P(resultado);
}

/*
 *  Rotina para cálculo do dígito verificador módulo 10 - FEBRABAN
 *  Recebe dois argumentos, número e dígito verificador na forma de caracteres.
 *  Retorna verdade se o dígito verificador estiver correto, falso caso
 *  contrário, ou nulo em caso de erro.
 */

PG_FUNCTION_INFO_V1(dv10);

Datum
dv10(PG_FUNCTION_ARGS) {
    int  digito = 0,    // Dígito verificador
         fator  = 2,    // Fator de multiplicação
         produto;       // Produto = dígito x fator
    text *num;          // Primeiro argumento = número
    text *dv;           // Segundo argumento = dígito verificador
    char *c;            // Ponteiro para os caracteres do número
    /* Verificar o recebimento de argumento nulo */
    if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
        PG_RETURN_NULL();
    /* Receber os argumentos */
    num = PG_GETARG_TEXT_P(0);
    dv  = PG_GETARG_TEXT_P(1);
    /* Verificar o recebimento de argumento vazio */
    if ((VARSIZE(num) == VARHDRSZ) || (VARSIZE(dv) == VARHDRSZ)) {
       PG_RETURN_NULL();
    }
    /* Verificar dígito verificador não dígito */
    if (!isdigit(*VARDATA(dv))) PG_RETURN_NULL();
    /* Calcular o dígito verificador */
    for (c = VAREND(num); c >= VARDATA(num); c--) {
        if (!isdigit(*c)) PG_RETURN_NULL(); // Verificar se é dígito
        produto = VALOR(*c) * fator;
        digito+= produto/10 + produto%10;
        if (--fator < 1) fator = 2;
    }
    digito = 10 - ( digito % 10 );
    if (digito == 10) digito = 0;
    /* Retornar verdade ou falso */
    PG_RETURN_BOOL (digito == VALOR(*VARDATA(dv)));
}

/*
 *  Rotina para cálculo do dígito verificador módulo 10 - FEBRABAN.
 *  Recebe um argumento, o número na forma de caracteres.
 *  Retorna o dígito verificador, ou nulo em caso de erro.
 */

PG_FUNCTION_INFO_V1(dv10dig);

Datum
dv10dig(PG_FUNCTION_ARGS) {
    int  digito = 0,    // Dígito verificador
         fator  = 2,    // Fator de multiplicação
         produto;       // Produto = dígito x fator
    text *num;          // Único argumento = número
    char *c;            // Ponteiro para os caracteres do número
    int32 tamanho;      // Tamanho do resultado da função
    text *resultado;    // Ponteiro para o resultado da função
    /* Verificar o recebimento de argumento nulo */
    if (PG_ARGISNULL(0))
        PG_RETURN_NULL();
    /* Receber o argumento */
    num  = PG_GETARG_TEXT_P(0);
    /* Verificar o recebimento de argumento vazio */
    if (VARSIZE(num) == VARHDRSZ) {
        PG_RETURN_NULL();
    }
    /* Calcular o dígito verificador */
    for (c = VAREND(num); c >= VARDATA(num); c--) {
        if (!isdigit(*c)) PG_RETURN_NULL(); // Verificar se é dígito
        produto = VALOR(*c) * fator;
        digito+= produto/10 + produto%10;
        if (--fator < 1) fator = 2;
    }
    digito = 10 - ( digito % 10 );
    if (digito == 10) digito = 0;
    /* Retornar o dígito verificador */
    tamanho = VARHDRSZ + sizeof(char);
    resultado = (text *) palloc(tamanho);
    memset((void *) resultado, 0, tamanho);
    VARATT_SIZEP(resultado) = tamanho;
    *VARDATA(resultado) = (char) DIGITO(digito);
    PG_RETURN_TEXT_P(resultado);
}

O código SQL para declarar estas funções é:

LOAD 'funcoes';

CREATE FUNCTION dv11(text,text) RETURNS boolean
    AS '$libdir/funcoes', 'dv11'
    LANGUAGE C STRICT;

CREATE FUNCTION dv11(text) RETURNS text
    AS '$libdir/funcoes', 'dv11dig'
    LANGUAGE C STRICT;

CREATE FUNCTION dv10(text,text) RETURNS boolean
    AS '$libdir/funcoes', 'dv10'
    LANGUAGE C STRICT;

CREATE FUNCTION dv10(text) RETURNS text
    AS '$libdir/funcoes', 'dv10dig'
    LANGUAGE C STRICT;

Utilização das funções com informações retiradas do código de barras de bloquetos de cobrança bancária:

SELECT dv11('9999101200000350007772130530150081897500000','1');

 dv11
------
 t
(1 linha)

SELECT dv11('9999101200000350007772130530150081897500000');

 dv11
------
 1
(1 linha)

SELECT dv10('0063504142','9');

 dv10
------
 t
(1 linha)

SELECT dv10('0063504142');

 dv10
------
 9
(1 linha)

Exemplo 31-2. Funções C para validar o número do CPF e do CNPJ

Neste exemplo são mostradas funções para validar o número do CPF e do CNPJ. A primeira função recebe o número do CPF com 8 a 11 dígitos, e a segunda recebe o número do CNPJ com 14 dígitos. Ambas retornam o valor booleano verdade se os dígitos verificadores estiverem corretos, ou falso caso contrário. Se o argumento for nulo, não tiver o número de dígitos esperado, ou contiver um dígito não numérico, as funções retornam nulo.

/*
 *  Rotina para validação do CPF.
 *  Recebe um argumento, o número do CPF com oito a onze dígitos.
 *  Retorna verdade se os dígitos verificadores do CPF estiverem corretos,
 *  falso caso contrário, ou nulo se o argumento for nulo, não tiver entre
 *  8 e 11 dígitos, ou contiver um dígito não numérico. Não serão considerados
 *  válidos os seguintes CPF: 000.000.000-00, 111.111.111-11, 222.222.222-22,
 *  333.333.333-33, 444.444.444-44, 555.555.555-55, 666.666.666-66,
 *  777.777.777-77, 888.888.888-88, 999.999.999-99.
 */

PG_FUNCTION_INFO_V1(cpf);

Datum
cpf(PG_FUNCTION_ARGS) {
    text *num;          // Único argumento = número do CPF
    bool iguais;        // Todos os dígitos são iguais
    int  fator,         // Fator de multiplicação
         digito;        // Dígito verificador
    char *cpf;          // Número do CPF com 11 dígitos
    char *c;            // Ponteiro para os caracteres do CPF
    /* Verificar o recebimento de argumento nulo */
    if (PG_ARGISNULL(0))
        PG_RETURN_NULL();
    /* Receber o argumento */
    num  = PG_GETARG_TEXT_P(0);
    /* Verificar se o CPF tem entre 8 e 11 dígitos */
    if ( ((VARSIZE(num) - VARHDRSZ) > 11*sizeof(char)) ||
         ((VARSIZE(num) - VARHDRSZ) <  8*sizeof(char))
       ) PG_RETURN_NULL();
    /* CPF com 11 dígitos */
    cpf = (char *) palloc(11*sizeof(char));               // Reservar memória
    strncpy (cpf, "00000000000", 11*sizeof(char));        // Preencher com zeros
    memcpy (cpf+11*sizeof(char)-(VARSIZE(num)-VARHDRSZ),  // Destino
            VARDATA(num),                                 // Origem
            VARSIZE(num)-VARHDRSZ);                       // Comprimento
    /* Verificar se todos os dígitos são iguais */
    iguais = true;
    for (c=cpf; c<cpf+9*sizeof(char); *c++) {
        if (*c != *(c+sizeof(char))) {
           iguais = false;
           break;
        }
    }
    if (iguais) PG_RETURN_BOOL(false);
    /* Validar o primeiro dígito verificador */
    for (c=cpf, digito=0, fator=10; fator>=2; fator--) {
        if (!isdigit(*c)) PG_RETURN_NULL(); // Retornar nulo se não for dígito
        digito += VALOR(*c++) * fator;
    }
    digito = 11 - ( digito % 11 );
    if (digito >= 10) digito = 0; // Restos 0 ou 1 digito = 0
    /* Retornar nulo se o primeiro dígito verificador não for um dígito */
    if (!isdigit(*c)) PG_RETURN_NULL();
    // Retornar falso se o primeiro dígito não estiver correto
    if (digito != VALOR(*c)) PG_RETURN_BOOL(false);
    /* Validar o segundo dígito verificador */
    for (c=cpf, digito=0, fator=11; fator>=2; fator--) {
        if (!isdigit(*c)) PG_RETURN_NULL(); // Retornar nulo se não for dígito
        digito += VALOR(*c++) * fator;
    }
    digito = 11 - ( digito % 11 );
    if (digito >= 10) digito = 0; // Restos 0 ou 1 digito = 0
    /* Retornar nulo se o segundo dígito verificador não for um dígito */
    if (!isdigit(*c)) PG_RETURN_NULL();
    // Retornar verdade ou falso de acordo com o segundo dígito verificador
    PG_RETURN_BOOL (digito == VALOR(*c));
}

/*
 *  Rotina para validação do CNPJ.
 *  Recebe um argumento, o número do CNPJ com quatorze dígitos.
 *  Retorna verdade se os dígitos verificadores do CNPJ estiverem corretos,
 *  falso caso contrário, ou nulo se o argumento for nulo, não tiver 14 dígitos,
 *  ou contiver um dígito não numérico.
 */

PG_FUNCTION_INFO_V1(cnpj);

Datum
cnpj(PG_FUNCTION_ARGS) {
    text *num;          // Único argumento = número do CNPJ
    int  fator,         // Fator de multiplicação
         digito;        // Dígito verificador
    char *c;            // Ponteiro para os caracteres do CNPJ
    /* Verificar o recebimento de argumento nulo */
    if (PG_ARGISNULL(0))
        PG_RETURN_NULL();
    /* Receber o argumento */
    num  = PG_GETARG_TEXT_P(0);
    /* Verificar se o CNPJ tem 14 dígitos */
    if ( (VARSIZE(num) - VARHDRSZ) != 14*sizeof(char) ) {
       PG_RETURN_NULL();
    }
    /* Validar o primeiro dígito verificador */
    for (c=VARDATA(num), digito=0, fator=13; fator>=2; fator--) {
        if (!isdigit(*c)) PG_RETURN_NULL(); // Retornar nulo se não for dígito
        digito += VALOR(*c++) * (fator>9 ? fator-8 : fator);
    }
    digito = 11 - ( digito % 11 );
    if (digito >= 10) digito = 0; // Restos 0 ou 1 digito = 0
    // Retornar falso se o primeiro dígito não estiver correto
    if (digito != VALOR(*c)) PG_RETURN_BOOL(false);
    /* Validar o segundo dígito verificador */
    for (c=VARDATA(num), digito=0, fator=14; fator>=2; fator--) {
        if (!isdigit(*c)) PG_RETURN_NULL(); // Retornar nulo se não for dígito
        digito += VALOR(*c++) * (fator>9 ? fator-8 : fator);
    }
    digito = 11 - ( digito % 11 );
    if (digito >= 10) digito = 0; // Restos 0 ou 1 digito = 0
    // Retornar verdade ou falso de acordo com o segundo dígito verificador
    PG_RETURN_BOOL (digito == VALOR(*c));
}

O código SQL para declarar estas funções é:

LOAD 'funcoes';

CREATE FUNCTION cpf(text) RETURNS boolean
    AS '$libdir/funcoes', 'cpf'
    LANGUAGE C STRICT;

CREATE FUNCTION cnpj(text) RETURNS boolean
    AS '$libdir/funcoes', 'cnpj'
    LANGUAGE C STRICT;

Os números de CPF utilizados para testar a função não estão divulgados por motivo de confidencialidade. Os números de CNPJ usados para testar a função são de órgãos públicos.

SELECT cnpj('42498634000166');

 cnpj
------
 t
(1 linha)

SELECT cnpj('42498733000148');

 cnpj
------
 t
(1 linha)

Exemplo 31-3. Função C para validar o número de inscrição eleitoral

Neste exemplo é mostrada uma função para validar o número de inscrição eleitoral. A função recebe como parâmetro o número de inscrição eleitoral, e retorna o valor booleano verdade se os dígitos verificadores estiverem corretos, ou falso caso contrário. Se o argumento for nulo, não tiver o número de dígitos esperado, ou contiver um dígito não numérico, a função retorna nulo. A função não verifica se a unidade da federação é válida.

A função é baseada na Resolução nº 20.132, de 19.03.98, do Tribunal Superior Eleitoral, art. 10, cujo texto é reproduzido abaixo:

Art. 10 - Os Tribunais Regionais Eleitorais farão distribuir, observada a seqüência numérica fornecida pela Secretaria de Informática, às Zonas Eleitorais da respectiva Circunscrição, séries de números de inscrição eleitoral, a serem utilizados na forma deste artigo.

Parágrafo único - O número de inscrição compor-se-á de até 12 (doze) algarismos, por Unidade da Federação, assim discriminados:

a) os 8 (oito) primeiros algarismos serão seqüenciados, desprezando-se, na emissão, os zeros à esquerda;

b) os 2 (dois) algarismos seguintes serão representativos da Unidade da Federação de origem da inscrição, conforme códigos constantes da seguinte tabela:

01 - São Paulo; 02 - Minas Gerais; 03 - Rio de Janeiro; 04 - Rio Grande do Sul; 05 - Bahia; 06 - Paraná; 07 - Ceará; 08 - Pernambuco; 09 - Santa Catarina; 10 - Goiás; 11 - Maranhão; 12 - Paraíba; 13 - Pará; 14 - Espírito Santo; 15 - Piauí; 16 - Rio Grande do Norte; 17 - Alagoas; 18 - Mato Grosso; 19 - Mato Grosso do Sul; 20 - Distrito Federal; 21 - Sergipe; 22 - Amazonas; 23 - Rondônia; 24 - Acre; 25 - Amapá; 26 - Roraima; 27 - Tocantins; 28 - Exterior (ZZ).

c) os 2 (dois) últimos algarismos constituirão dígitos verificadores, determinados com base no módulo 11 (onze), sendo o primeiro calculado sobre o número seqüencial e o último sobre o código da Unidade da Federação seguido do primeiro dígito verificador.

Código fonte da função:

/*
 *  Rotina para validação do número de inscrição eleitoral.
 *  Recebe um argumento, o número de inscrição eleitoral com doze dígitos.
 *  Retorna verdade se os dígitos verificadores do número de inscrição
 *  eleitoral estiverem corretos, falso caso contrário, ou nulo se o argumento
 *  for nulo, não tiver 12 dígitos, ou contiver um dígito não numérico.
 */

PG_FUNCTION_INFO_V1(nie);

Datum
nie(PG_FUNCTION_ARGS) {
    text *num;          // Único argumento = número de inscrição eleitoral
    int  fator,         // Fator de multiplicação
         digito;        // Dígito verificador
    char *nie;          // Número do inscrição eleitoral com 12 dígitos
    char *c;            // Ponteiro para os caracteres do número de inscrição
    /* Verificar o recebimento de argumento nulo */
    if (PG_ARGISNULL(0))
        PG_RETURN_NULL();
    /* Receber o argumento */
    num  = PG_GETARG_TEXT_P(0);
    /* Verificar se o número de inscrição tem entre 10 e 12 dígitos */
    if ( ((VARSIZE(num) - VARHDRSZ) > 12*sizeof(char)) ||
         ((VARSIZE(num) - VARHDRSZ) < 10*sizeof(char))
       ) PG_RETURN_NULL();
    /* Número de inscrição eleitoral com 12 dígitos */
    nie = (char *) palloc(12*sizeof(char));               // Reservar memória
    strncpy (nie, "000000000000", 12*sizeof(char));       // Preencher com zeros
    memcpy (nie+12*sizeof(char)-(VARSIZE(num)-VARHDRSZ),  // Destino
            VARDATA(num),                                 // Origem
            VARSIZE(num)-VARHDRSZ);                       // Comprimento
    /* Validar o primeiro dígito verificador */
    for (c=nie, digito=0, fator=9; fator>=2; fator--) {
        if (!isdigit(*c)) PG_RETURN_NULL(); // Retornar nulo se não for dígito
        digito += VALOR(*c++) * fator;
    }
    digito = 11 - ( digito % 11 );
    if (digito >= 10) digito = 0; // Restos 0 ou 1 digito = 0
    // Retornar falso se o primeiro dígito não estiver correto
    if (digito != VALOR(*(c+2*sizeof(char)))) PG_RETURN_BOOL(false);
    /* Validar o segundo dígito verificador */
    for (digito=0, fator=4; fator>=2; fator--) {
        if (!isdigit(*c)) PG_RETURN_NULL(); // Retornar nulo se não for dígito
        digito += VALOR(*c++) * fator;
    }
    digito = 11 - ( digito % 11 );
    if (digito >= 10) digito = 0; // Restos 0 ou 1 digito = 0
    // Retornar verdade ou falso de acordo com o segundo dígito verificador
    PG_RETURN_BOOL (digito == VALOR(*c));
}

O código SQL para declarar esta função é:

LOAD 'funcoes';

CREATE FUNCTION nie(text) RETURNS boolean
    AS '$libdir/funcoes', 'nie'
    LANGUAGE C STRICT;

Os números de inscrição eleitoral utilizados para testar a função não estão divulgados por motivo de confidencialidade.

Exemplo 31-4. Função C para calcular o Máximo Divisor Comum

Neste exemplo é mostrada uma função para calcular o máximo divisor comum (MDC) entre dois números inteiros, utilizando o Algoritmo Euclidiano.

/*
 *  Rotina para cálculo do Máximo Divisor Comum (MDC).
 *  Utiliza o Algoritmo de Euclides ou Algoritmo Euclidiano.
 *  Recebe com parâmetro dois números inteiros e retorna o MDC.
 *  Fonte: http://www2.fundao.pro.br/articles.asp?cod=151
 */

PG_FUNCTION_INFO_V1(mdc);

Datum
mdc(PG_FUNCTION_ARGS) {
    int  a,     // Primeiro número
         b,     // Segundo número
         r,     // Resto da divisão de A por B
         t;     // Armazenamento temporário (troca)
    /* Verificar o recebimento de argumento nulo */
    if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
        PG_RETURN_NULL();
    /* Receber os argumentos */
    a = PG_GETARG_INT32(0);
    b = PG_GETARG_INT32(1);
    /* Garantir que A seja o maior valor */
    if ( a < b ) {
       t = a;
       a = b;
       b = t;
    }
    /* Calcular o MDC */
    while ( b != 0 )
    {
      r = a % b;
      a = b;
      b = r;
    }
    PG_RETURN_INT32(a);
}

O código SQL para declarar esta função é:

LOAD 'funcoes';

CREATE FUNCTION mdc(int, int) RETURNS int
    AS '$libdir/funcoes', 'mdc'
    LANGUAGE C STRICT;

Exemplos de utilização da função:

SELECT mdc(144,1024);

 mdc
-----
  16
(1 linha)

SELECT mdc(8192,224);

 mdc
-----
  32
(1 linha)

Exemplo 31-5. Função C para concatenar duas cadeias de caracteres

Neste exemplo é mostrada uma função para concatenar duas cadeias de caracteres.

/*
 * Função para concatenar duas cadeias de caracteres.
 * Se encontra no arquivo src/tutorial/funcs_new.c da
 * distribuição do código fonte do PostgreSQL.
 */

PG_FUNCTION_INFO_V1(concat_text);

Datum
concat_text(PG_FUNCTION_ARGS)
{
    text    *arg1 = PG_GETARG_TEXT_P(0);
    text    *arg2 = PG_GETARG_TEXT_P(1);
    int32   tam_novo_texto = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
    text    *novo_texto = (text *) palloc(tam_novo_texto);

    memset((void *) novo_texto, 0, tam_novo_texto);
    VARATT_SIZEP(novo_texto) = tam_novo_texto;
    strncpy(VARDATA(novo_texto), VARDATA(arg1), VARSIZE(arg1) - VARHDRSZ);
    strncat(VARDATA(novo_texto), VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ);
    PG_RETURN_TEXT_P(novo_texto);
}

O código SQL para declarar esta função é:

LOAD 'funcoes';

CREATE FUNCTION concat(text, text) RETURNS text
    AS '$libdir/funcoes', 'concat_text'
    LANGUAGE C STRICT;

Exemplos de utilização da função:

SELECT concat('Postgre','SQL');

   concat
------------
 PostgreSQL
(1 linha)

SELECT concat(concat('ae','io'),'u') AS vogais;

 vogais
--------
 aeiou
(1 linha)

Exemplo 31-6. Listar as funções C definidas pelo usuário

A consulta abaixo lista o nome, número de argumentos e o tipo retornado por todas as funções C definidas pelo usuário.

SELECT n.nspname, p.proname, p.pronargs, format_type(t.oid, null) as return_type
  FROM pg_namespace n, pg_proc p,
       pg_language l, pg_type t
  WHERE p.pronamespace = n.oid
    and n.nspname not like 'pg\\_%'       -- sem catálogos
    and n.nspname != 'information_schema' -- sem information_schema
    and p.prolang = l.oid
    and p.prorettype = t.oid
    and l.lanname = 'c'
  ORDER BY nspname, proname, pronargs, return_type;

 nspname | proname | pronargs | return_type
---------+---------+----------+-------------
 public  | cnpj    |        1 | boolean
 public  | concat  |        2 | text
 public  | cpf     |        1 | boolean
 public  | dv10    |        1 | text
 public  | dv10    |        2 | boolean
 public  | dv11    |        1 | text
 public  | dv11    |        2 | boolean
 public  | mdc     |        2 | integer
 public  | nie     |        1 | boolean
(9 linhas)

Notas

[1]

pg_config --pkglibdir retorna /usr/local/pgsql/lib na plataforma utilizada. (N. do T.)

[2]

pg_config --includedir-server retorna /usr/local/pgsql/include/server na plataforma utilizada. (N. do T.)

SourceForge.net Logo CSS válido!