/* * $Id: funcoes.c,v 1.5 2005/11/15 19:25:42 halleypo Exp $ */ #include "postgres.h" /* declarações gerais do PostgreSQL */ #include "fmgr.h" /* macros dos argumentos/resultados */ #define VALOR(char) ((char) - '0') #define DIGITO(val) ((val) + '0') #define VAREND(__PTR) (VARDATA(__PTR) + VARSIZE(__PTR) - VARHDRSZ - sizeof(char)) /* Protótipos para prevenir contra possíveis advertências do gcc. */ Datum dv11(PG_FUNCTION_ARGS); Datum dv11dig(PG_FUNCTION_ARGS); Datum dv10(PG_FUNCTION_ARGS); Datum dv10dig(PG_FUNCTION_ARGS); Datum cpf(PG_FUNCTION_ARGS); Datum cnpj(PG_FUNCTION_ARGS); Datum nie(PG_FUNCTION_ARGS); Datum mdc(PG_FUNCTION_ARGS); Datum concat_text(PG_FUNCTION_ARGS); /* * 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); } /* * 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=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)); } /* * 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)); } /* * 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); } /* * 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); }