A última parte da manipulação de cursores é a forma de acessar os dados que foram retornados no nosso cursor. Uma coisa que não temos que nos preocupar é como os dados serão retornados, pois a libpq sempre retorna os dados em forma de string e cabe a quem estiver escrevendo o programa converter os dados, caso seja necessário.
Somente no caso de cursores binários (BINARY CURSORS) esta afirmação não é verdade, mas este assunto está fora de nosso escopo.
Vamos conhecer as funções que irão nos auxiliar com a tarefa de acessar os dados retornados:
PQgetvalue
Esta função retorna uma string terminada em NULL, e tem o seguinte protótipo:
char *PQgetvalue(const PGresult *res, int tup_num, int field_num);
Onde *res é um ponteiro para uma estrutura PGresult, tup_num é o número da linha que queremos acessar e field_num é o número do campo. Lembrando que número de linhas e colunas sempre começam em zero.
Um ponto importante sobre esta função: a string retornada encontra-se dentro de uma estrutura PGresult, logo, é preciso copiar os dados caso seja necessário manter os dados para fazer qualquer coisa com eles.
PQgetisnull
Esta função nos permite verificar se o valor a ser retornado pelo banco é uma string de comprimento nulo ou uma string em branco cujo campo contém o valor NULL (lembrando que NULL dentro de uma coluna no banco não significa vazio, e sim desconhecido).
int PQgetisnull(const PGresult *res, int tup_num, int field_num);
Onde *res é um ponteiro para uma estrutura PGresult, tup_num é o número da linha que queremos acessar e field_num é o número do campo. Lembrando que número de linhas e colunas sempre começam em zero. A função irá retornar 1 se o campo for nulo e 0 se tiver um valor não-nulo.
Vamos ao nosso código agora. Vamos criar uma função para poder ver os dados retornados:
void Exibe_Dados(PGresult *result)
{
int coluna;
for(coluna = 0; coluna < PQnfields(result); coluna++)
{
/*Verifica se o valor da coluna é nulo*/
if(PQgetisnull(result, 0, coluna))
{
printf("DATA: <NULL>
");
}
else
{
printf("DATA: %s
", PQgetvalue(result, 0, coluna));
}
}
}
A listagem completa do programa, incluindo a rotina de conexão fica assim:
#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>
/*Objeto de conexão*/
PGconn *conn = NULL;
/*Protótipo de funções*/
int ExecutaComando(const char *, PGresult **);
void Mostra_Info_Colunas(PGresult *);
void Exibe_Dados(PGresult *);
int main()
{
int comando_ok;
PGresult *result;
/*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;
}
comando_ok = ExecutaComando("BEGIN WORK", &result);
if(comando_ok)
{
PQclear(result);
/*Executa o comando*/
comando_ok = ExecutaComando("DECLARE curr CURSOR FOR SELECT * FROM contatos", &result);
if(comando_ok)
{
PQclear(result);
comando_ok = ExecutaComando("FETCH 1 IN curr", &result);
if(comando_ok)
Mostra_Info_Colunas(result);
while(comando_ok && PQntuples(result) > 0)
{
Exibe_Dados(result);
PQclear(result);
ExecutaComando("FETCH NEXT IN curr", &result);
}
}
comando_ok = ExecutaComando("COMMIT WORK", &result);
}
if(comando_ok)
PQclear(result);
/*Verifica se a conexão está aberta e a encerra*/
if(conn != NULL)
PQfinish(conn);
}
int ExecutaComando(const char *comando, PGresult **ptr_resultado)
{
int codigo_retorno = 1;
const char *str_resultado;
PGresult *resultado_local;
printf("
Executando comando: %s
", comando);
/*executa o comando e armazena localmente*/
resultado_local = PQexec(conn, comando);
/*passa o resultado local para o segundo parâmetro da função, para que seja
acessível dentro de MAIN*/
*ptr_resultado = resultado_local;
/*Verifica se o comando foi bem sucedido*/
if(!resultado_local)
{
/*Se falhou, imprime mensagem na tela e seta o código de retorno da função como 0 (erro)*/
printf("O comando falhou.
");
codigo_retorno = 0;
}
else
{
/*Se foi sucedido, chamamos PQresultStatus para verificar qual o código de retorno*/
switch(PQresultStatus(resultado_local))
{
case PGRES_COMMAND_OK:
printf("Comando ok, %s linhas afetadas.
", PQcmdTuples(resultado_local));
break;
case PGRES_TUPLES_OK:
printf("A query retornou %d linhas.
", PQntuples(resultado_local));
break;
default:
printf("Error in query: %s
", PQresultErrorMessage(resultado_local));
PQclear(resultado_local);
codigo_retorno = 0;
break;
}
}
/*retorna código de retorno*/
return codigo_retorno;
}
void Mostra_Info_Colunas(PGresult *result)
{
int numero_colunas;
int i;
if(!result)
return;
/*Obtém o número de colunas*/
numero_colunas = PQnfields(result);
printf("O conjunto de dados tem %d colunas
", numero_colunas);
for(i = 0; i <numero_colunas; i++)
{
printf("Campo: %d.
Nome: %s
Tamanho Interno: %d
", i, PQfname(result, i), PQfsize(result, i));
}
}
void Exibe_Dados(PGresult *result)
{
int coluna;
for(coluna = 0; coluna < PQnfields(result); coluna++)
{
/*Verifica se o valor da coluna é nulo*/
if(PQgetisnull(result, 0, coluna))
{
printf("DATA: <NULL>
");
}
else
{
printf("DATA: %s
", PQgetvalue(result, 0, coluna));
}
}
}
Considerações finais
A utilização da libpq é bem mais simples do que parece. Neste artigo e no anterior, vimos como fazer quase todas as ações possíveis pela libpq, claro, ainda existem outros tópicos a serem abordados, mas com o que temos até o momento, já temos o suficiente para escrever uma aplicação completa.
Um grande abraço!!!