PostgreSQL - Embutindo comandos SQL no seu código C

Existe uma alternativa ao uso de bibliotecas para acesso ao PostgreSQL, que é a inclusão de comandos SQL dentro do próprio código em C. Veremos neste artigo como fazer esta integração.

[ Hits: 68.106 ]

Por: Poleto em 01/06/2006


Retornando dados de tabelas



Executar comandos SELECT usando o ECPG tem uma pequena diferença com relação a outros comandos. Ao executar um SELECT, precisamos de uma maneira de armazenar o que foi retornado pelo SELECT em algum lugar. Uma variável para ser mais exato.

Quando executamos um SELECT, existem duas situações que podem ocorrer:
  • Vamos executar um SELECT que retorna apenas uma linha (e temos certeza de que ele vai retornar apenas uma linha);
  • Vamos executar um SELECT que retorna mais de uma linha.

No primeiro caso, a solução é (relativamente) simples. Vamos ver um exemplo de código:

EXEC SQL BEGIN DECLARE SECTION;
   int var_id_contato;
   VARCHAR var_nome_contato[255];
EXEC SQL END DECLARE SECTION;

EXEC SQL SELECT id_contato, nome INTO :var_id_contato, :var_nome_contato FROM contatos WHERE id_contato = 1;

         if(sqlca.sqlcode != 0) {
   printf(" Erro executando a query. ");
   printf("Descrição: %s ", sqlca.sqlerrm.sqlerrmc);
   return -1;
} else {
   printf("O id do contato é: %d ", var_id_contato);
   printf("O nome do contato é: %s ", var_nome_contato.arr);
}

O que temos de diferente no comando acima basicamente é a palavra INTO, que faz parte da especificação SQL do PostgresQL. Isso fará com que as variáveis var_id_contato e var_nome_contato sejam preenchidas com os campos id_contato e nome, respectivamente.

Outra novidade é que declaramos a variável var_nome_contato sendo do tipo VARCHAR. Mas espera aí, existe o tipo VARCHAR em C? A resposta é não! Mas felizmente, o pré-processador do ECPG nos permite essa declaração, pois o tipo VARCHAR do PostgreSQL não tem nenhum correlato em C (nem mesmo o tipo char!). O que acontece quando o código em C é gerado após ser processado pelo ECPG é que esta declaração será transformada em uma estrutura, na seguinte forma:

struct varchar_var_nome_contato {
   int len;
   char arr[255];
} var_nome_contato;

Portanto, podemos acessar o valor retornado usando var_nome_contato.arr.

O segundo caso é um pouco mais trabalhoso, mas nada de outro mundo. Ocorre quando não sabemos quantas linhas o SELECT irá retornar. Para isso vamos usar um cursor para retornar os dados e ter acesso a eles. Vamos ao código:

EXEC SQL BEGIN DECLARE SECTION;
   int var_id_contato;
   VARCHAR var_nome_contato[255];
EXEC SQL END DECLARE SECTION;

EXEC SQL BEGIN WORK;

EXEC SQL DECLARE curr_contatos CURSOR FOR SELECT id_contato, nome FROM contatos;

EXEC SQL OPEN curr_contatos;

EXEC SQL FETCH NEXT IN curr_contatos INTO :var_id_contato, :var_nome_contato;

if(sqlca.sqlcode != 0) {
   printf(" Erro executando a query. ");
   printf("Descrição: %s ", sqlca.sqlerrm.sqlerrmc);
   return -1;
}

while( sqlca.sqlcode == 0) {
   printf("O id do contato é: %d ", var_id_contato);
   printf("O nome do contato é: %s ", var_nome_contato.arr);

      EXEC SQL FETCH NEXT IN curr_contatos INTO :var_id_contato, :var_nome_contato;
}

EXEC SQL CLOSE curr_contatos;
EXEC SQL COMMIT WORK;

No código acima omitimos algumas verificações de erro para tornar o código mais simples. Vamos analisar os comandos do código acima:

EXEC SQL DECLARE curr_contatos CURSOR FOR SELECT id_contato, nome FROM contatos;

Este comando vai declarar um cursor (escrevi um artigo aqui no Viva o Linux que detalha melhor os cursores no PostgreSQL) chamado curr_contatos para o SELECT que acompanha o comando.

EXEC SQL OPEN curr_contatos;

Este comando simplesmente abre o cursor.

EXEC SQL FETCH NEXT IN curr_contatos INTO :var_id_contato, :var_nome_contato;

O comando acima vai realizar o primeiro fetch, caso haja algum dado na tabela, e vai armazenar os valores dentro das variáveis var_id_contato e var_nome_contato.

Após o FETCH, vamos entrar em um loop while, que vai ser executado enquanto não tivermos um erro ou um warning. Antes que você pense que ficaremos em um loop infinito, vamos lembrar que quando uma query não retorna resultados o valor de sqlca.sqlcode é maior do que zero. Dentro do loop, precisamos executar outro FETCH, pois se não o fizermos aí sim entraremos em um loop infinito.

Por fim, fechamos o cursor com o comando CLOSE curr_contatos e executamos o COMMIT.

Um ponto que omitimos foi a verificação no caso do PostgreSQL retornar valores nulos (NULL do banco de dados, não NULL do C) para algum dos campos. Esta verificação deve ser feita, pois do contrário podemos receber erros do tipo:

NULL value without indicator in line xx.

Para contornar este problema, precisamos usar um "indicator" (ou indicador, traduzindo literalmente) para verificar se uma coluna é NULL ou não. Para isso, declaramos uma variável extra na seção DECLARE do tipo int. Vejamos como vai ficar o código acima, supondo que temos algum valor NULL no campos nome:

EXEC SQL BEGIN DECLARE SECTION;
   int var_id_contato;
   VARCHAR var_nome_contato[255];
   int verifica_nome_null;
EXEC SQL END DECLARE SECTION;

EXEC SQL BEGIN WORK;

EXEC SQL DECLARE curr_contatos CURSOR FOR SELECT id_contato, nome FROM contatos;

EXEC SQL OPEN curr_contatos;

EXEC SQL FETCH NEXT IN curr_contatos INTO :var_id_contato, :var_nome_contato:verifica_nome_null;

if(sqlca.sqlcode != 0) {
   printf(" Erro executando a query. ");
   printf("Descrição: %s ", sqlca.sqlerrm.sqlerrmc);
   return -1;
}

while( sqlca.sqlcode == 0) {
   printf("O id do contato é: %d ", var_id_contato);
   printf("O nome do contato é: %s ", var_nome_contato.arr);

      EXEC SQL FETCH NEXT IN curr_contatos INTO :var_id_contato, :var_nome_contato:verifica_nome_null;
}

EXEC SQL CLOSE curr_contatos;
EXEC SQL COMMIT WORK;

Se a variável verifica_nome_null for igual a zero, então o campo não é NULL e podemos acessar este valor, do contrário, se tentarmos acessar este valor teremos um erro. A verificação pode ser feita assim:

while( sqlca.sqlcode == 0) {
   if(verifica_nome_null == 0) {
      printf("O id do contato é: %d ", var_id_contato);
      printf("O nome do contato é: %s ", var_nome_contato.arr);
   }

      EXEC SQL FETCH NEXT IN curr_contatos INTO :var_id_contato, :var_nome_contato;
}

Para fazer a verificação em mais de uma coluna, basta criar mais variáveis do tipo int e fazer a associação conforme mostrado aqui.

Página anterior     Próxima página

Páginas do artigo
   1. Introdução
   2. Criando o ambiente de testes
   3. Iniciando com o ECPG
   4. Conectando com o servidor de banco de dados
   5. Compilando os programas
   6. Inserindo, atualizando e apagando dados de tabelas
   7. Retornando dados de tabelas
   8. Considerações Finais
Outros artigos deste autor

Acessando PostgreSQL com C - Cursores

Instalando o CMS Drupal 4.7

Acessando PostgreSQL com C

Leitura recomendada

Ensaio acerca de bibliotecas de código aberto para abstração de acesso a banco de dados em linguagem C++

Acessando PostgreSQL com C - Cursores

Usando MySQL na linguagem C

Acessando PostgreSQL com C

Embutindo um banco de dados SQLite em sua aplicação C++

  
Comentários
[1] Comentário enviado por marcolinux em 07/06/2006 - 17:11h

Parece que foi DEUS que mandou vc postar este artigo!

Estou justamente sofrendo aqui usando a libpq !


Parabéns pelo material.

MARCOLINUX

[2] Comentário enviado por faustojacome em 10/03/2008 - 23:43h

Apos quebrar muito a cabeca para funcionar no Debian com o PostgreSQL e o ecpg via apt-get aqui vai a forma que consegui compilar.

gcc -g -I/usr/include/postgresql/ -o saida postgreSQL.c -L/usr/lib/postgresql/8.3/lib/ -lecpg -lpq

[3] Comentário enviado por benwin em 28/10/2009 - 07:28h

Porque eu não posso usar variáveis no campo FROM?

EXEC SQL SELECT usuario, senha INTO :varusuario, :varsenha FROM :tabelabd WHERE usuario = :meunumerodb;

Existe alguma forma de burlar isso?

Abraço!


Contribuir com comentário