Você está na página 1de 8

Acessando PostgreSQL com C

Autor: Poleto <luiz.poleto at gmail.com> Data: 08/12/2005 Introduo

O PostgreSQL um banco de dados extremamente eficiente, que no deixa a desejar se comparado com os grandes bancos de dados comerciais. Neste artigo iremos ver, ainda que de forma bem simplificada, uma das muitas formas de interao com este banco de dados, que usando a linguagem C, atravs de uma interface de programao disponibilizada pelo PostgreSQL.

Criando o ambiente de testes Antes de comearmos, vamos partir do princpio que o PostgreSQL j funciona em seu computador e que voc tem um banco de dados pronto para ser usado. Tambm iremos considerar que voc j tem um leve conhecimento sobre comandos SQL. No nosso caso, estamos usando um banco chamado 'TESTE'. No entraremos em detalhes sobre esses assuntos, pois isto material suficiente para um outro artigo. Vamos criar o nosso ambiente de testes usando o psql, que a ferramenta de linha de comando do PostgreSQL. Caso no saiba us-la, bem simples, basta digitar o comando "psql" na linha de comando, finalizando com um ponto-e-vrgula (;). Para o nosso ambiente de testes, iremos precisar apenas de uma tabela. Iremos cri-la com o comando abaixo: CREATE TABLE contatos( email varchar(255), nome varchar(255) ); Digitando no psql, teremos o seguinte (a palavra 'TESTE' que aparece o nome do nosso banco de dados): TESTE=> TESTE=> TESTE=> TESTE=> TESTE=> CREATE TABLE contatos ( email varchar(255), nome varchar(255) );

(note que o comando s executado quando temos um ';'). Ao pressionar enter no final do comando acima, teremos o seguinte resultado: teste=> CREATE TABLE Pronto, nossa tabela est criada e pronta para ser usada.

Libpq Antes de comearmos a nossa codificao, vamos dar uma olhada na biblioteca acima. Esta a biblioteca do PostgreSQL que nos permite interagir com o banco de dados usando a linguagem C. Basicamente, esta biblioteca vai nos permitir realizar conexes e disparar comandos no banco de dados. Para usar as funes contidas nesta biblioteca, temos que: a. Incluir o arquivo de cabealho libpq-fe.h Isso simples, basta utilizar a diretiva #include, como mostrado abaixo: #include <libpq-fe.h> b. Indicar ao gcc o diretrio em que se encontra esta biblioteca; Primeiro voc vai precisar descobrir onde se encontra o arquivo libpq-fe.h (se voc utilizar o comando "whereis libpq-fe.h", voc vai conseguir descobrir onde se encontra este arquivo), depois, ao compilar o programa, passamos o diretrio acima dentro da opo -I do gcc: $ gcc -o programa -I/usr/include/pgsql programa.c -lpq Isso supondo que a sua biblioteca est em /usr/include/pgsql. Altere para o diretrio onde se encontra a sua. Talvez seja necessrio utilizar a opo -L e o diretrio, ao invs da opo -I. No meu caso, para facilitar, copiei o arquivo para o diretrio de include do meu Slackware (no meu caso, /usr/include), assim no preciso incluir a opo -I ao chamar o gcc.

Rotinas para conexo O mtodo mais usado para conexo com o PostgreSQL o comando PQconnectdb (existem outras rotinas, mas focaremos apenas nesta), que tem a forma: PGconn *PQconnectdb(const char *conninfo); Esta funo abre uma nova conexo usando os parmetros passados na string conninfo. Esses parmetros so passados separados por espaos, e podem ser: host: O nome do host que queremos nos conectar, neste exemplo, usaremos localhost; port: Porta de conexo com o PostgreSQL, como usaremos a porta padro, podemos omitir este parmetro; dbname: O nome do banco de dados que iremos nos conectar. Se deixado em branco, ser usado o banco de nome igual ao usurio logado no Linux; user: O nome do usurio a realizar a conexo. Se omitido, o padro o mesmo usurio logado no Linux; password: A senha para o usurio a ser usado. Cada um dos parmetros acima seguido de um sinal de igual ('=') e depois pelo valor que ele deve assumir. Parmetros omitidos usaro o valor padro. Vejamos agora um exemplo para conexo com o nosso banco de dados TESTE criado anteriormente:

PGconn *conn = NULL; conn = PQconnectdb("host=localhost dbname=TESTE"); Primeiro declaramos uma varivel *conn, do tipo PGconn com o valor inicial NULL, em seguida, chamamos a funo PQconnectdb passando como parmetros o host, que ser o localhost e o banco ser o TESTE, que criamos acima. A funo PQconnectdb ir retornar NULL somente se houver falha na alocao de memria para o objeto de conexo. Mesmo que o retorno seja no-NULL, precisamos verificar se a conexo foi bem sucedida. Podemos fazer isso com a funo PQstatus, que tem o prottipo: ConnStatusType PQstatus(PGconn *conn); Esta funo recebe como parmetro um objeto PGconn e retorna um tipo enumerado, que pode ter dois valores: CONNECTION_OK ou CONNECTION_BAD (os significados so bem bvios). Continuando o nosso exemplo, aps chamar a funo PQconnectdb, verificamos se a conexo foi bem sucedida: if(PQstatus(conn) == CONNECTION_OK) { printf("Conexo com efetuada com sucesso."); } else { printf("Falha na conexo."); PQfinish(conn); return -1; } No h muito o que se comentar sobre o cdigo acima. Chamamos a funo PQstatus passando como parmetro o nosso objeto de conexo (conn) e verificamos o retorno. Se CONNECTION_OK, a conexo com o banco foi efetuada com sucesso, do contrrio, mostramos a mensagem que nossa conexo falhou, e chamamos a funo PQfinish que tem o seguinte prottipo: void PQfinish(PGconn *conn); Esta funo encerra a conexo com o banco de dados e tambm libera a memria usada pelo objeto de conexo, e deve ser chamada mesmo que a conexo no tenha ocorrido com sucesso. Vale dizer que uma vez chamada esta funo, no se deve tentar usar o objeto conn novamente. Alm de saber que a nossa conexo falhou, podemos querer saber qual foi a mensagem de erro retornada pelo PostgreSQL, podemos fazer isso com a funo PQerrorMessage, que tem o seguinte prottipo: char *PQerrorMessage(PGconn *conn); Esta funo retorna um ponteiro char, e recebe como parmetro um objeto de conexo. Vamos agora ver a listagem do cdigo: #include <libpq-fe.h> /*Objeto de conexo*/ PGconn *conn = NULL; int main() { /*realiza a conexo*/ conn = PQconnectdb("host=localhost dbname=TESTE"); if(PQstatus(conn) == CONNECTION_OK)

{ printf("Conexo com efetuada com sucesso. "); } else { printf("Falha na conexo. Erro: %s", PQerrorMessage(conn)); PQfinish(conn); return -1; } /*Verifica se a conexo est aberta e a encerra*/ if(conn != NULL) PQfinish(conn); } Pronto, agora, se a conexo falhar, podemos ter uma descrio melhor.

Inserindo, atualizando e removendo dados A manipulao de dados no PostgreSQL extremamente simples. Apenas a chamada a uma funo para executar o comando e algumas outras para verificar os resultados so o suficiente. A funo a ser chamada para executar o comando a PQexec, que tem o seguinte prottipo: PGresult *PQexec(PGconn *conn, const char *sql_string); Esta funo recebe como parmetro um objeto de conexo e uma string contendo o comando SQL a ser executado. O resultado pode ser um NULL em casos excepcionais, do contrrio, receberemos um ponteiro que pode ser verificado chamando a funo PQresultStatus, que receber como parmetro o ponteiro retornado por PQexec e ir retornar um enum do tipo ExecStatusType. A funo PQresultStatus tem o seguinte prottipo: ExecStatusType *PQresultStatus(PGresult *result); Os valores da enum retornada por esta funo so: PGRES_EMPTY_QUERY - Nada foi feito PGRES_COMMAND_OK - O comando foi bem sucedido, mas sem dados retornados. PGRES_TUPLES_OK - O comando foi bem sucedido e dados podem ter sido retornados. PGRES_COPY_OUT - Uma cpia para um arquivo externo estava em andamento. PGRES_COPY_IN - Uma cpia de um arquivo externo estava em andamento. PGRES_BAD_RESPONSE - Erro inesperado. PGRES_NONFATAL_ERROR - Erro no fatal. PGRES_FATAL_ERROR - Erro fatal. Algumas observaes quanto aos valores acima: PGRES_TUPLES_OK significa que um comando SELECT foi bem sucedido, mas no necessariamente que dados foram retornados. Os valores de PGRES_COPY_IN e PGRES_COPY_OUT esto relacionados com o banco de dados que est sendo carregado ou sendo feito o backup. O valor de retorno da funo PQresultStatus nos mostra apenas a ao ocorrida, se quisermos mais detalhes quanto ao resultado, podemos chamar uma outra funo, PQresultErrorMessage, que tem o seguinte prottipo: const char *PQresultErrorMessage(PGresult *result); Esta funo recebe o mesmo parmetro que a funo PQresultStatus, porm o retorno uma string contendo a mensagem retornada pelo banco. Uma outra funo que pode ter alguma utilidade a funo PQcmdTuples, que retorna o nmero de linhas afetadas por comandos INSERT, DELETE e UPDATE (para descobrir a quantidade de linhas retornadas por um SELECT existe uma outra funo). A funo PQcmdTuples tem o seguinte prottipo:

const char *PQcmdTuples(PGresults *result); O parmetro de entrada desta funo o mesmo das funes acima, o ponteiro retornado por PQexec, e o retorno, uma string terminada com NULL contendo dgitos em formato de caracteres (ateno a isto, pois a funo no retorna um inteiro, como seria mais bvio). Para finalizar esta seo, precisamos saber como liberar o objeto de resultado que obtivemos com a funo PQexec. Para isso, usamos a funo PQclear, que tem o seguinte prottipo: void PQclear(PQresult *result); Vamos agora ver na prtica como usamos tudo isso: #include <stdio.h> #include <libpq-fe.h> /*Objeto de conexo*/ PGconn *conn = NULL; /*Ponteiro de resultado*/ PGresult *result; int main() { /*realiza a conexo*/ conn = PQconnectdb("host=localhost dbname=TESTE"); if(PQstatus(conn) == CONNECTION_OK) { printf("Conexo com efetuada com sucesso. "); } else { printf("Falha na conexo. Erro: %s", PQerrorMessage(conn)); PQfinish(conn); return -1; } /*Executa o comando*/ /*Aqui voc substitui a query por outras. Colocamos apenas uma como exemplo.*/ result = PQexec(conn, "INSERT INTO contatos (email, nome) VALUES ('Luiz Poleto', 'blah@hehe.com')"); if(!result) { printf("Erro executando comando. "); } else { switch(PQresultStatus(result)) { case PGRES_EMPTY_QUERY: printf("Nada aconteceu. "); 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; } /*Libera o nosso objeto*/ PQclear(result); }

/*Verifica se a conexo est aberta e a encerra*/ if(conn != NULL) PQfinish(conn); } Recuperando dados Chegamos na parte mais usada, e tambm a mais complexa da libpq, que a recuperao de dados. O que torna essa parte da API mais complexa o fato de no sabermos sempre quantas linhas o nosso comando ir retornar, ou mesmo a quantidade de campos. Porm, podemos fazer chamadas adicionais a funes da API que ajudam a contornar este problema. Para executar um comando SELECT continuamos a usar a funo PQexec exatamente como antes. Primeiro, vamos descobrir quantas linhas o nosso comando retorna. Para isso, usamos a funo PQntuples, que tem o seguinte prottipo: int PQntuples(PGresult *result); Onde result o retorno da chamada a funo PQexec. Tudo bem, isso na verdade no ajuda muito, pois o que queremos ver o que temos dentro da tabela. Para isso, vamos usar uma maneira rpida e simples da libpq que gera a sada em um arquivo. Esta funo a PQprint, que tem o prottipo: void PQprint(FILE *stream, PGresult *result, PQprintOpt *options); Esta funo vai receber como parmetros um ponteiro para o nosso arquivo, um ponteiro de resultado retornado pela funo PQexec e uma estrutura de opes. Esta estrutura vai definir algumas opes para gerar o arquivo, como por exemplo o caractere que vai delimitar os dados. typedef struct PQprintOpt { pqbool header; /*imprime o cabealho*/ pqbool align; /*alinha e preenche os campos*/ pqbool standard; /*formato antigo no mais utilizado*/ pqbool html3; /*gera sada das tabelas em HTML*/ pqbool expanded; /*expande tabelas*/ pqbool pager; /*usa o paginador para sada caso necessrio*/ char *fieldSep; /*caractere que ser o separador de campos*/ char *tableOpt; /*insero na <table ...> HTML*/ char *legenda; /*legenda HTML*/ char **fieldname; /*array terminado com null contendo o nome de campos para substituio*/ } 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 conexo*/

PGconn *conn = NULL; /*Ponteiro de resultado*/ PGresult *result; /*Arquivo que ir conter o fluxo de sada*/ FILE *output_stream; /*Estrutura de opes*/ PQprintOpt print_options; int main() { /*realiza a conexo*/ conn = PQconnectdb("host=localhost dbname=TESTE"); if(PQstatus(conn) == CONNECTION_OK) { printf("Conexo com efetuada com sucesso. "); } else { printf("Falha na conexo. 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 sada*/ 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 conexo est aberta e a encerra*/ if(conn != NULL) PQfinish(conn); } Percebam que apesar de ser relativamente simples de usar, esta soluo est longe de ser a melhor soluo para recuperar dados. Quando temos um volume pequeno de dados esta soluo aceitvel, mas que torna-se inutilizvel 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 porm material suficiente para um novo artigo, devido ao tamanho e complexidade envolvida.

Consideraes finais Neste artigo vimos de forma bem simplificada e resumida como podemos interfacear o PostgreSQL atravs da linguagem C. Muito mais pode ser feito com esta interface ao PostgreSQL alm do que foi visto aqui, mas este artigo tem a inteno de ser apenas um pontap para os muitos recursos que nos oferecido pela Libpq. Um grande abrao!!!

http://www.vivaolinux.com.br/artigo/Acessando-PostgreSQL-com-C Voltar para o site

Você também pode gostar