Acessando PostgreSQL com C
O PostgreSQL é um dos principais bancos de dados open source do momento. Veremos neste artigo como acessá-lo utilizando a linguagem C.
Parte 6: Recuperando dados
Chegamos na parte mais usada, e também a mais complexa da libpq, que é a recuperação de dados.
O que torna essa parte da API mais complexa é o fato de não sabermos sempre quantas linhas o nosso comando irá retornar, ou mesmo a quantidade de campos. Porém, podemos fazer chamadas adicionais a funções da API que ajudam a contornar este problema. Para executar um comando SELECT continuamos a usar a função PQexec exatamente como antes.
Primeiro, vamos descobrir quantas linhas o nosso comando retorna. Para isso, usamos a função PQntuples, que tem o seguinte protótipo:
int PQntuples(PGresult *result);
Onde result é o retorno da chamada a função PQexec.
Tudo bem, isso na verdade não ajuda muito, pois o que queremos é ver o que temos dentro da tabela. Para isso, vamos usar uma maneira rápida e simples da libpq que gera a saída em um arquivo. Esta função é a PQprint, que tem o protótipo:
void PQprint(FILE *stream, PGresult *result, PQprintOpt *options);
Esta função vai receber como parâmetros um ponteiro para o nosso arquivo, um ponteiro de resultado retornado pela função PQexec e uma estrutura de opções. Esta estrutura vai definir algumas opções para gerar o arquivo, como por exemplo o caractere que vai delimitar os dados.
typedef struct PQprintOpt
{
pqbool header; /*imprime o cabeçalho*/
pqbool align; /*alinha e preenche os campos*/
pqbool standard; /*formato antigo não mais utilizado*/
pqbool html3; /*gera saída das tabelas em HTML*/
pqbool expanded; /*expande tabelas*/
pqbool pager; /*usa o paginador para saída caso necessário*/
char *fieldSep; /*caractere que será o separador de campos*/
char *tableOpt; /*inserção na <table ...> HTML*/
char *legenda; /*legenda HTML*/
char **fieldname; /*array terminado com null contendo o nome de campos para substituição*/
}
Vamos agora alterar o nosso programa anterior para recuperar os dados da nossa tabela de testes.
O que torna essa parte da API mais complexa é o fato de não sabermos sempre quantas linhas o nosso comando irá retornar, ou mesmo a quantidade de campos. Porém, podemos fazer chamadas adicionais a funções da API que ajudam a contornar este problema. Para executar um comando SELECT continuamos a usar a função PQexec exatamente como antes.
Primeiro, vamos descobrir quantas linhas o nosso comando retorna. Para isso, usamos a função PQntuples, que tem o seguinte protótipo:
int PQntuples(PGresult *result);
Onde result é o retorno da chamada a função PQexec.
Tudo bem, isso na verdade não ajuda muito, pois o que queremos é ver o que temos dentro da tabela. Para isso, vamos usar uma maneira rápida e simples da libpq que gera a saída em um arquivo. Esta função é a PQprint, que tem o protótipo:
void PQprint(FILE *stream, PGresult *result, PQprintOpt *options);
Esta função vai receber como parâmetros um ponteiro para o nosso arquivo, um ponteiro de resultado retornado pela função PQexec e uma estrutura de opções. Esta estrutura vai definir algumas opções para gerar o arquivo, como por exemplo o caractere que vai delimitar os dados.
typedef struct PQprintOpt
{
pqbool header; /*imprime o cabeçalho*/
pqbool align; /*alinha e preenche os campos*/
pqbool standard; /*formato antigo não mais utilizado*/
pqbool html3; /*gera saída das tabelas em HTML*/
pqbool expanded; /*expande tabelas*/
pqbool pager; /*usa o paginador para saída caso necessário*/
char *fieldSep; /*caractere que será o separador de campos*/
char *tableOpt; /*inserção na <table ...> HTML*/
char *legenda; /*legenda HTML*/
char **fieldname; /*array terminado com null contendo o nome de campos para substituição*/
}
Vamos agora alterar o nosso programa anterior para recuperar os dados da nossa tabela de testes.
#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>
/*Objeto de conexão*/
PGconn *conn = NULL;
/*Ponteiro de resultado*/
PGresult *result;
/*Arquivo que irá conter o fluxo de saída*/
FILE *output_stream;
/*Estrutura de opções*/
PQprintOpt print_options;
int main()
{
/*realiza a conexão*/
conn = PQconnectdb("host=localhost dbname=TESTE");
if(PQstatus(conn) == CONNECTION_OK)
{
printf("Conexão com efetuada com sucesso. ");
}
else
{
printf("Falha na conexão. Erro: %s", PQerrorMessage(conn));
PQfinish(conn);
return -1;
}
/*Executa o comando*/
result = PQexec(conn, "SELECT * FROM contatos");
if(!result)
{
printf("Erro executando comando. ");
}
else
{
switch(PQresultStatus(result))
{
case PGRES_EMPTY_QUERY:
printf("Nada aconteceu. ");
break;
case PGRES_TUPLES_OK:
printf("A query retornou %d linhas. ", PQntuples(result));
break;
case PGRES_FATAL_ERROR:
printf("Error in query: %s ", PQresultErrorMessage(result));
break;
case PGRES_COMMAND_OK:
printf("%s linhas afetadas. ", PQcmdTuples(result));
break;
default:
printf("Algum outro resultado ocorreu. ");
break;
}
/*Define o nosso arquivo de saída*/
output_stream = fopen("/dev/tty", "w");
if(output_stream == NULL)
printf("Erro abrindo o arquivo. ");
else
{
memset(&print_options, '\0', sizeof(print_options));
print_options.header = 1;
print_options.align = 1;
print_options.html3 = 0;
print_options.fieldSep = "|";
print_options.fieldName = NULL;
PQprint(output_stream, result, &print_options);
}
/*Libera o nosso objeto*/
PQclear(result);
}
/*Verifica se a conexão está aberta e a encerra*/
if(conn != NULL)
PQfinish(conn);
}
#include <stdlib.h>
#include <libpq-fe.h>
/*Objeto de conexão*/
PGconn *conn = NULL;
/*Ponteiro de resultado*/
PGresult *result;
/*Arquivo que irá conter o fluxo de saída*/
FILE *output_stream;
/*Estrutura de opções*/
PQprintOpt print_options;
int main()
{
/*realiza a conexão*/
conn = PQconnectdb("host=localhost dbname=TESTE");
if(PQstatus(conn) == CONNECTION_OK)
{
printf("Conexão com efetuada com sucesso. ");
}
else
{
printf("Falha na conexão. Erro: %s", PQerrorMessage(conn));
PQfinish(conn);
return -1;
}
/*Executa o comando*/
result = PQexec(conn, "SELECT * FROM contatos");
if(!result)
{
printf("Erro executando comando. ");
}
else
{
switch(PQresultStatus(result))
{
case PGRES_EMPTY_QUERY:
printf("Nada aconteceu. ");
break;
case PGRES_TUPLES_OK:
printf("A query retornou %d linhas. ", PQntuples(result));
break;
case PGRES_FATAL_ERROR:
printf("Error in query: %s ", PQresultErrorMessage(result));
break;
case PGRES_COMMAND_OK:
printf("%s linhas afetadas. ", PQcmdTuples(result));
break;
default:
printf("Algum outro resultado ocorreu. ");
break;
}
/*Define o nosso arquivo de saída*/
output_stream = fopen("/dev/tty", "w");
if(output_stream == NULL)
printf("Erro abrindo o arquivo. ");
else
{
memset(&print_options, '\0', sizeof(print_options));
print_options.header = 1;
print_options.align = 1;
print_options.html3 = 0;
print_options.fieldSep = "|";
print_options.fieldName = NULL;
PQprint(output_stream, result, &print_options);
}
/*Libera o nosso objeto*/
PQclear(result);
}
/*Verifica se a conexão está aberta e a encerra*/
if(conn != NULL)
PQfinish(conn);
}
Percebam que apesar de ser relativamente simples de usar, esta solução está longe de ser a melhor solução para recuperar dados. Quando temos um volume pequeno de dados esta solução é aceitável, mas que torna-se inutilizável quando temos tabelas maiores. Para piorar, é muito complicado se quisermos manipular os valores retornados.
Existe uma outra alternativa muito mais eficiente para retornarmos dados que é o uso de cursores. Este assunto porém é material suficiente para um novo artigo, devido ao tamanho e complexidade envolvida.
eu imaginava ser mais complicado, pelo fato do unico banco que acessei usando C foi o oracle, qual tinha que dar algumas voltas ate gerar o binario