33.7. 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.

33.7.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 33.7.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 houver necessidade de 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 localizaçã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.

33.7.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 33-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 33-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 or int16 postgres.h
int2vector int2vector* postgres.h
integer (int4) int4 or 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.

33.7.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, haveria necessidade de 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.

33.7.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() guarante 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 33.7.9 ), implementar funções de gatilho ( Capítulo 35 ) e tratadores de chamada de linguagem procedural ( Capítulo 47 ). 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 mais informações deve ser visto o arquivo src/backend/utils/fmgr/README na distribuição do código fonte.

33.7.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:

33.7.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
ou
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 33.7.1 onde o servidor espera encontrar os arquivos de biblioteca compartilhada.

33.7.7. 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 nome = 'João' OR nome = 'José';

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(TupleTableSlot *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)
{
    TupleTableSlot  *t = (TupleTableSlot *) PG_GETARG_POINTER(0);
    int32            limite = PG_GETARG_INT32(1);
    bool isnull;
    int32 salario;

    salario = DatumGetInt32(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(salario > limite);
}

GetAttributeByName é a função de sistema do PostgreSQL que retorna atributos da linha especificada. Possui três argumentos: o argumento do tipo TupleTableSlot* 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.

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;

33.7.8. 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"

O suporte a retorno de tipos de dado compostos (ou linhas) começa pela estrutura AttInMetadata. Esta estrutura mantém matrizes contendo informações individuais dos atributos, necessárias para criar uma linha a partir de cadeias de caracteres C. A informação contida nesta estrutura é derivada da estrutura TupleDesc, mas é armazenada para evitar processamento redundante em cada chamada à função que retorna conjunto (veja a próxima seção). No caso da função que retorna conjunto a estrutura AttInMetadata deve ser processada uma vez durante a primeira chamada, e salva para ser usada novamente nas próximas chamadas. AttInMetadata também mantém um ponteiro para a TupleDesc original.

typedef struct AttInMetadata
{
    /* TupleDesc completa */
    TupleDesc       tupdesc;

    /* matriz de tipo de atributo da função de entrada finfo */
    FmgrInfo       *attinfuncs;

    /* matriz de tipo do atributo typelem */
    Oid            *attelems;

    /* matriz de atributo typmod */
    int32          *atttypmods;
}   AttInMetadata;

Para ajudar no preenchimento desta estrutura estão disponíveis diversas funções e macros. Use

TupleDesc RelationNameGetTupleDesc(const char *relname)

para obter a TupleDesc para uma relação nomeada, ou

TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases)

para obter a TupleDesc baseada em um OID de tipo. Pode ser utilizado para obter a TupleDesc para um tipo base ou composto. Então

AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)

retorna um ponteiro para uma estrutura AttInMetadata, inicializada com base na TupleDesc fornecida. AttInMetadata pode ser utilizada em conjunto com cadeias de caracteres C para produzir um valor de linha formado apropriadamente (tupla chamada internamente).

Para retornar uma tupla deve ser criado um encaixe (slot) de tupla baseado em TupleDesc. Pode ser utilizado

TupleTableSlot *TupleDescGetSlot(TupleDesc tupdesc)

para inicializar o encaixe da tupla, ou ser obtido um através de outro (fornecido pelo usuário) meio. O encaixe da tupla é necessário para criar o tipo Datum a ser retornado pela função O mesmo encaixe pode (e deve) ser reutilizado a cada chamada.

Após construir a estrutura AttInMetadata,

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

pode ser utilizada para construir a HeapTuple a partir de dados dos usuários fornecidos na forma de cadeias de caracteres C. valores é uma matriz de cadeias de caracteres C, uma para cada atributo da linha retornada. Cada cadeia de caracteres C deve estar na forma esperada pela função de entrada do tipo de dado do atributo. Para retornar o valor nulo para um dos atributos, o ponteiro correspondente na matriz valores deve ser definido como NULL. É necessário chamar esta função novamente para cada linha retornada.

A construção da tupla utilizando TupleDescGetAttInMetadata e BuildTupleFromCStrings somente é conveniente quando a função computa naturalmente os valores a serem retornados como textos em cadeias de caractere. Se o código computar naturalmente os valores como um conjunto de valores Datum, deve ser utilizada a função subjacente heap_formtuple para converter os valores Datum diretamente na tupla. Ainda serão necessárias TupleDesc e TupleTableSlot, mas não AttInMetadata.

Após ter sido construída a tupla a ser retornada pela função, esta deve ser convertida em Datum. Use

TupleGetDatum(TupleTableSlot *slot, HeapTuple tuple)

para obter Datum a partir de uma tupla e um encaixe. Este Datum pode ser retornado diretamente se houver intenção de retornar uma única linha, ou pode ser utilizado como o valor de retorno corrente em uma função que retorna conjunto.

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

33.7.9. 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
     *
     * o encaixe é utilizado para retornar tuplas (ou seja, tipos de dado
     * compostos), não sendo necessário para retornar tipos de dado base.
     */
    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;
} 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;
    outras declarações conforme necessário

    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
            obter o encaixe
            funcctx->encaixe = encaixe;
        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;
    TupleTableSlot      *slot;
    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");

        /* alocar um encaixe para a tupla com esta tupdesc */
        slot = TupleDescGetSlot(tupdesc);

        /* atribuir o encaixe ao contexto da função */
        funcctx->slot = slot;

        /*
         * 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;
    slot = funcctx->slot;
    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 armazenamento no encaixe.
         * Deve ser uma matriz de cadeias de caracteres C a serem
         * processadas posteriormente pelas funções de entrada do tipo.
         */
        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 = TupleGetDatum(slot, 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.

33.7.10. 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 33.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 */
    elemento = 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 camando 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.

33.7.11. Exemplos de funções escritas em C

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

Os exemplos desta seção foram elaborados com o arquivo fonte funcoes.c , o arquivo objeto intermediário funcoes.o, e o arquivo de biblioteca compartilhada funcoes.so, colocados em /usr/lib/pgsql/. O código fonte da distribuição do PostgreSQL foi descompactado sob o diretório /tmp. Com isto, a geração do arquivo de biblioteca compartilhada foi feita usando os comandos:

-- Configurar a árvore de fontes
cd /tmp/postgresql-7.4.1/
./configure
-- Gerar a biblioteca compartilhada
cd /usr/lib/pgsql/
gcc -fpic -c funcoes.c -I /tmp/postgresql-7.4.1/src/include/
gcc -shared -o funcoes.so funcoes.o

Exemplo 33-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 (veja a Seção 33.8 ): 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.
 *  Retorna verdade se o dígito verificador estiver
 *  correto, ou falso caso contrário.
 */

PG_FUNCTION_INFO_V1(dv11);

Datum
dv11(PG_FUNCTION_ARGS) {
    int  digito=0, fator=2;
    text *num  = PG_GETARG_TEXT_P(0); // Primeiro argumento = número
    text *dv   = PG_GETARG_TEXT_P(1); // Segundo argumento = dígito verificador
    /* Verificar o recebimento de argumento vazio */
    if ((VARATT_SIZEP(num) == VARHDRSZ) || (VARATT_SIZEP(num) == VARHDRSZ)) {
       PG_RETURN_NULL();
    }
    /* Verificar dígito verificador não dígito */
    if (!isdigit(*VARDATA(dv))) PG_RETURN_NULL();
    /* Calcular o dígito verificador */
    char *c;
    for (c = VARLAST(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. Retorna o dígito verificador.
 */

PG_FUNCTION_INFO_V1(dv11dig);

Datum
dv11dig(PG_FUNCTION_ARGS) {
    int  digito=0, fator=2;
    text *num  = PG_GETARG_TEXT_P(0); // Único argumento = número
    /* Verificar o recebimento de argumento vazio */
    if (VARATT_SIZEP(num) == VARHDRSZ) {
       PG_RETURN_NULL();
    }
    /* Calcular o dígito verificador */
    char   *c;
    for (c = VARLAST(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 */
    int32  tamanho = VARHDRSZ + sizeof(char);
    text *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.
 *  Retorna verdade se o dígito verificador estiver
 *  correto, ou falso caso contrário.
 */

PG_FUNCTION_INFO_V1(dv10);

Datum
dv10(PG_FUNCTION_ARGS) {
    int  digito=0, fator=2, produto;
    text *num  = PG_GETARG_TEXT_P(0);   // Primeiro argumento = número
    text *dv   = PG_GETARG_TEXT_P(1);   // Segundo argumento = dígito verificador
    /* Verificar o recebimento de argumento vazio */
    if ((VARATT_SIZEP(num) == VARHDRSZ) || (VARATT_SIZEP(num) == VARHDRSZ)) {
       PG_RETURN_NULL();
    }
    /* Verificar dígito verificador não dígito */
    if (!isdigit(*VARDATA(dv))) PG_RETURN_NULL();
    /* Calcular o dígito verificador */
    char *c;
    for (c = VARLAST(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. Retorna o dígito verificador.
 */

PG_FUNCTION_INFO_V1(dv10dig);

Datum
dv10dig(PG_FUNCTION_ARGS) {
    int  digito=0, fator=2, produto;
    text *num  = PG_GETARG_TEXT_P(0); // Único argumento = número
    /* Verificar o recebimento de argumento vazio */
    if (VARATT_SIZEP(num) == VARHDRSZ) {
       PG_RETURN_NULL();
    }
    /* Calcular o dígito verificador */
    char *c;
    for (c = VARLAST(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 */
    int32  tamanho = VARHDRSZ + sizeof(char);
    text *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('3419112820459284738210122301001623770000034439','7');

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

SELECT dv11('3419112820459284738210122301001623770000034439');

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

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

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

SELECT dv10('0063504142');

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

Exemplo 33-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 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 onze dígitos.
 *  Retorna verdade se os dígitos verificadores do CPF
 *  estiverem corretos, ou falso caso contrário.
 *  Se o argumento for nulo, não tiver 11 dígitos, ou contiver
 *  um dígito não numérico, retorna nulo.
 */

PG_FUNCTION_INFO_V1(cpf);

Datum
cpf(PG_FUNCTION_ARGS) {
    int  fator, digito;
    char *c;
    text *num  = PG_GETARG_TEXT_P(0); // Primeiro argumento = número do CPF
    /* Verificar o recebimento de argumento vazio */
    if ((VARATT_SIZEP(num) == VARHDRSZ) || (VARATT_SIZEP(num) == VARHDRSZ)) {
       PG_RETURN_NULL();
    }
    /* Verificar se o CPF tem 11 dígitos */
    if ( (VARSIZE(num) - VARHDRSZ) != 11*sizeof(char) ) {
       PG_RETURN_NULL();
    }
    /* Validar o primeiro dígito verificador */
    for (c=VARDATA(num), 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 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=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 verdade ou falso
    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, ou falso caso contrário.
 *  Se o argumento for nulo, não tiver 14 dígitos, ou contiver
 *  um dígito não numérico, retorna nulo.
 */

PG_FUNCTION_INFO_V1(cnpj);

Datum
cnpj(PG_FUNCTION_ARGS) {
    int  fator, digito;
    char *c;
    text *num  = PG_GETARG_TEXT_P(0); // Primeiro argumento = número do CNPJ
    /* Verificar o recebimento de argumento vazio */
    if ((VARATT_SIZEP(num) == VARHDRSZ) || (VARATT_SIZEP(num) == VARHDRSZ)) {
       PG_RETURN_NULL();
    }
    /* 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
    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 33-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, ou falso caso contrário.
 *  Se o argumento for nulo, não tiver 12 dígitos, ou contiver
 *  um dígito não numérico, retorna nulo.
 */

PG_FUNCTION_INFO_V1(nie);

Datum
nie(PG_FUNCTION_ARGS) {
    int  fator, digito;
    char *c;
    text *num  = PG_GETARG_TEXT_P(0); // Primeiro argumento = número de inscrição
    /* Verificar o recebimento de argumento vazio */
    if ((VARATT_SIZEP(num) == VARHDRSZ) || (VARATT_SIZEP(num) == VARHDRSZ)) {
       PG_RETURN_NULL();
    }
    /* Verificar se o número de inscrição tem 12 dígitos */
    if ( (VARSIZE(num) - VARHDRSZ) != 12*sizeof(char) ) {
       PG_RETURN_NULL();
    }
    /* Validar o primeiro dígito verificador */
    for (c=VARDATA(num), 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
    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 33-4. 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  | cpf                  |        1 | boolean
 public  | dv10                 |        1 | text
 public  | dv10                 |        2 | boolean
 public  | dv11                 |        1 | text
 public  | dv11                 |        2 | boolean
 public  | nie                  |        1 | boolean
 public  | plpgsql_call_handler |        0 | language_handler
(8 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/include/postgresql/server na plataforma utilizada. (N. do T.)

SourceForge.net Logo