Como acessar o PostgreSQL com C – Cursores

Acessando o PostgreSQL com C

Introdução

O PostgreSQL é um banco de dados extremamente eficiente, que não deixa a desejar se comparado com os grandes bancos de dados comerciais. Neste artigo iremos ver a recuperação de dados através da linguagem C, usando a libpq de uma forma alternativa para a recuperação de dados, com a utilização de cursores.

Criando o ambiente de testes

Primeiramente estamos levando em consideração que o PostgreSQL já está instalado e funcionando. Também presume-se que você tenha algum conhecimento utilizando a libpq, pelo menos como iniciar uma conexão com o banco de dados e como executar comandos.
Para os testes, usaremos aqui o mesmo ambiente criado no artigo
A única alteração necessária será a adição de dados na tabela criada. Caso não saiba como fazê-lo, de uma olhada no artigo citado anteriormente.

Uma breve explicação sobre cursores

Um cursor é uma forma de percorrer um conjunto de resultados, geralmente retornados através de um comando select.Um cursor sempre aponta para uma linha dentro do conjunto de dados retornados e, com o auxilio da libpq, podemos além de obter os valores, obter também informações sobre colunas, o que pode ser útil em alguns casos.
Uma outra vantagem da utilização de cursores é a redução da quantidade de memória exigida pelo programa que executou o select. Imagine uma consulta que retorne milhões de registros, isso exigiria uma enorme quantidade de memória alocada para armazenar o resultado da consulta. Além disso, geralmente estaremos trabalhando através de uma rede e a quantidade de dados que estará sendo transmitido pela rede será enorme.
Quando usamos a forma tradicional para recuperação de dados, o PostgreSQL obtém todos os dados da consulta e então os transmite pela rede de uma vez. Ao utilizar cursores, apenas uma linha é transmitida de cada vez conforme o cursor avança ou retrocede dentro do conjunto de dados. Isso, porém, pode se tornar um problema quando temos um conjunto de dados muito grande.

Declaração de cursores

Agora que já vimos bastante teoria, é hora de colocar tudo isso em prática. Vale ressaltar que não entraremos em detalhes sobre como conectar com o PostgreSQL ou sobre como executar comandos pela libpq, uma vez que existe um artigo aqui sobre este assunto:
O primeiro passo é a declaração do cursor, que em código SQL, tem a seguinte sintaxe:
DECLARE nome_do_cursor CURSOR FOR comando_select
Isso cria e automaticamente abre um cursor que tem o nome especificado em nome_do_cursor. O cursor fica então vinculado ao comando de select SQL que foi especificado em comando_select. Para codificar o nosso programa, vamos declarar uma variável que irá conter o resultado do comando PQexec (e claro, consideramos que já existe uma conexão aberta com o banco, aqui chamada de conn):
/*Ponteiro de resultado*/ PGresult *result;
e a nossa chamada a função PQexec irá ficar assim:
result = PQexec(conn, "DECLARE curr CURSOR FOR SELECT * FROM contatos");
Até aqui, a única novidade é a declaração do cursor. Vale observar que operações com cursores devem sempre estar entre blocos de transações (assunto grande para ser tratado aqui, nos limitaremos a listar os comandos apenas).Após a declaração do cursor, iremos recuperar os dados retornados (se algum). Esta operação tem o nome de FETCH, e tem a seguinte sintaxe em SQL:
FETCH [FORWARD|BACKWARD] [number|ALL|NEXT] [IN nome_do_cursor];
Onde:
FORWARD e BACKWARD indicam a direção que o cursor irá ler os dados, para frente e para trás, respectivamente. O padrão é FORWARD e normalmente é omitido;
number, ALL e NEXT indicam quantas linhas iremos recuperar: number deve ser trocado por um número qualquer (10, por exemplo, que indica que 10 linhas serão lidas por vez no cursor). ALL recupera todos os dados e NEXT movimenta o cursor fazendo-o apontar para a próxima linha.
IN indica o cursor em que daremos o fetch.
No nosso caso, podemos declarar o fetch da seguinte forma:
result = PQexec(conn, "FETCH ALL IN curr");
Agora que já temos material o suficiente para criar um cursor e dar um fetch, vamos ver como fica o nosso programa. Omitimos o ponto onde fazemos a conexão com o banco de dados.
result = PQexec(conn, "BEGIN WORK"); /*Executa o comando*/
result = PQexec(conn, "DECLARE curr CURSOR FOR SELECT * FROM contatos"); if(!result)
{
   printf("Erro executando comando. ");
}
else
{
   PQclear(result);
   result = PQexec(conn, "FETCH ALL IN curr");
   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;
   }
   result = PQexec(conn, "COMMIT WORK");
          
   /*Libera o nosso objeto de resultado*/
   PQclear(result);
}
Algumas observações sobre o código acima: os comandos 'BEGIN WORK' e 'COMMIT WORK' iniciam e finalizam uma transação, respectivamente. O programa acima irá apenas imprimir na tela a quantidade de linhas que foram retornadas pelo comando select.

Retornando uma linha por vez

Antes de darmos os próximos passos com cursores, vamos alterar nosso código para ficar um pouco mais legível, uma vez que para retornar uma linha de cada vez teremos que fazer a verificação do retorno da função PQexec cada vez que ela for chamada (ou seja, uma vez por linha!), o que resultaria em um código cheio de switch, o que ficaria "feio", mas dizer assim. Criaremos uma função que ficará assim:
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;
}
Esta função não tem nenhum mistério, tudo aqui já foi visto, apenas colocamos a execução de um comando e a verificação de retorno dentro da função.Agora que nosso código vai ficar mais legível, vamos continuar com o básico. Vamos no código abaixo retornar apenas uma linha por vez. Não há mistério nisso, lembra que na declaração do FETCH podemos dizer quantos dados queremos retornar? Nosso código vai ficar assim (lembrando que estamos omitindo a parte aonde abrimos e fechamos a conexão com o banco):
    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);
          
          while(comando_ok && PQntuples(result) > 0)
          {
             PQclear(result);
             ExecutaComando("FETCH NEXT IN curr", &result);
          }
       }
      comando_ok = ExecutaComando("COMMIT WORK", &result);
    }     if(comando_ok)
       PQclear(result);
A saída do programa será algo assim (no meu caso, a minha tabela tem 5 linhas):Executando comando: BEGIN WORK
Comando ok, linhas afetadas.Executando comando: DECLARE curr CURSOR FOR SELECT * FROM contatos
Comando ok, linhas afetadas.
Executando comando: FETCH 1 IN curr
A query retornou 1 linhas.
Executando comando: FETCH NEXT IN curr
A query retornou 1 linhas.
Executando comando: FETCH NEXT IN curr
A query retornou 1 linhas.
Executando comando: FETCH NEXT IN curr
A query retornou 1 linhas.
Executando comando: FETCH NEXT IN curr
A query retornou 1 linhas.
Executando comando: FETCH NEXT IN curr
A query retornou 0 linhas.
Executando comando: COMMIT WORK
Comando ok, linhas afetadas.
Note que quando o cursor atinge o final do conjunto de dados, o comando FETCH NEXT irá retornar zero.

Informações sobre colunas

Uma das utilidades de se usar um cursor pela libpq é obter informações sobre as colunas retornadas pelo cursor. Podemos saber tanto o tamanho da coluna quanto o nome da coluna. Para isso, precisamos conhecer três novas funções, que são:

PQnfields

Esta função nos permite saber quantas colunas foram retornadas na query passada ao cursor. O protótipo desta função é:
int PQnfields(const PGresult *res);

PQfname

Esta função nos permite saber o nome de uma determinada coluna, associada ao field_num, aonde a primeira coluna começa no zero. O protótipo desta função é:
char *PQfname(const PGresult *res, int field_num);

PQfsize

Esta função nos permite ter uma idéia do tamanho do campo. Não temos como saber precisamente pois esta função retorna o tamanho em bytes usado internamente pelo PostgreSQL e mesmo assim, para tipos de dados variáveis como VARCHAR esta função retorna -1. O protótipo desta função é:
int PQfsize(const PGresult *res, int field_num);
Uma boa idéia é sempre especificar pelo nome a coluna que você precisa acessar, evitando que quando novas colunas forem acrescentadas o seu programa se perca na quantidade de campos. Para descobrir o índice de um campo através do nome da coluna, podemos usar a função PQfnumber, que tem o seguinte protótipo:
int PQfnumber(const PGresult *res, const char *field_name);
Esta função retorna -1 quando o nome passado como parâmetro não é reconhecido como nome de coluna.
Conhecidas estas funções, é hora de colocar a teoria em prática. Vamos ver como fica o nosso código. Primeiro, vamos criar uma função que irá mostrar as informações sobre as colunas:
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));
   }
}
Depois, a nossa função main vai ficar assim:
    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)
          {
             PQclear(result);
             ExecutaComando("FETCH NEXT IN curr", &result);
          }
       }
      comando_ok = ExecutaComando("COMMIT WORK", &result);
    }     if(comando_ok)
       PQclear(result);
A saída deve ficar assim (estamos mostrando somente a saída para a função Mostra_Info_Colunas):O conjunto de dados tem 2 colunas
Campo: 0.
Nome: email
Tamanho Interno: -1Campo: 1.
Nome: nome
Tamanho Interno: -1

Acessando os dados recuperados

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!!!

Autor: Poleto [ads-post]

COMENTÁRIOS

Nome

.net Foundation,1,1. TheNewBoston,1,2048,1,32 bits,1,3CX Phone System,1,3D,5,4.8.5,1,4MLinux,5,4MLinux 20.0,1,4MLinux 21.0,1,4MParted,1,4MParted 20.0,1,4MRecover,2,64 bits,1,Aaron Swartz,1,Acessibilidade,3,Acesso Remoto,1,Administração,1,Adobe,2,Adobeair,2,Aegisub,1,Agenda,1,AI,1,AIDE,1,AirDroid,2,AirMore,1,AKER,1,Alien,1,Alpha,1,alphaOS,1,Alpine Linux,1,ALT Linux,1,Alternativos,3,Amazon,1,AMD,5,Anatel,1,Android,52,Android 6.0,1,Android 6.0.1,1,Android 7.1,1,Android 7.1 Nougat,1,Android Marshmallow,1,Android Studio,2,Andromeda OS,1,AngryIPScanner,1,Antergos,2,Antivírus,3,Antivirus Live CD,1,antiX,1,Apache,2,Apachetop,1,Aplicativos,3,Apostilas,5,Apowersoft Phone Manager,1,App Grid,1,Apple,8,Applet,4,Apport,1,Apps Android,2,ApricityOS,2,APT,5,Apt-Fast,1,Aptik,1,Arch Linux,38,Arch Linux 2016.11.01,1,Arch Linux 2016.12.01,1,ArchBang,1,Archey,1,ArchStrike,1,Arduíno,1,ArduPilot,1,armhf,1,Artes Gráficas,1,Artigo,16,Artigos,148,Assembly Language,1,Asterisk,1,Atheros,1,Atom,4,Atraci,1,Audacity,1,Audio Recorder,1,Aulas Gráficas,1,Auto-apt,1,AutoCAD,3,Avidemux,1,Avisos,11,AWS,1,Azure,1,Backdoor,1,Backup,2,BakAndImgCD,1,BananaPi,1,Banco de dados,1,Banda Larga,1,Bash,1,Bateria,2,BDC,1,Bio-Linux,1,BIOS,4,Birdfont,1,Bitcoin,3,BitLocker,1,Bitorrent,2,BitSight,1,BitTorrent Sync,1,Black Arch Linux,3,Black Lab Linux,1,Black Lab Linux 8.0,1,blackPanther,1,Blackphone,1,Bleachbit,2,Blender,2,Blender Foundation,1,BloatWare,1,Bloqueio,1,Bludit,1,Bludit CMS,1,Bluestar Linux,1,Bodhi linux,5,Bodhi Linux 4.0.0,1,Boot,3,Bootavel,1,Boston Globe,1,Botão Hibernar,1,Botnet,1,BQ Aquaris M10,1,Braille,1,Brasil,3,Bricks,1,BRL-CAD,1,Browser,1,Brute Force,1,BSD,9,BSD Games,1,BSDO,1,Btrfs,1,Budgie,3,budgie-remix,1,Bug,1,Build Linux,1,Buscador,2,BusenLabs,1,Businness Intelligence,1,C,1,C#,1,C++,1,Café com,2,Caixa Econômica,1,Caledonia,1,Calibre,1,Campus Party,2,Canaima,1,Canary,1,Canonical,36,Canonical.Ubuntu,9,Cartuchos,3,Celestia,1,Censura,1,CentOS,13,CentOS 6,1,CentOS 7,1,Central de Programas,1,Chakra,4,Chalet OS,1,Choqok,1,Chrome,1,Chrome 54,1,Chrome Beta,1,Chrome Canary,1,Chrome Dev,1,Chrome OS,2,Chromebooks,2,Chromecast,2,Chromecast Ultra,2,Chromium,1,CIA,2,Cinnamon,9,Cinnamon 3.2,5,Cinnamon 3.2.1,3,Cinnamon 3.2.2,1,Cisco,1,Cisco Packet Tracer,1,ClamAV,1,ClamTK,1,ClassicMenu Indicator,2,Clement Lefebvre,2,Cliente Blog,1,Cliente e-mail,4,Cliente FTP,1,Cliente Git,2,ClipGrab,1,Clonezilla,1,Cloud,3,CMS,3,COBOL,1,Código Aberto,2,Collabora,1,Comandos,7,Comandos Terminal,42,Como Corrigir erro,5,Compactação,4,Compiz,1,Complementos,1,Computador,4,Conhecimento,1,Consulta Publica,1,Conversores,4,Converter,3,Cookies,1,Coreia do Norte,1,CoreOS,1,Corrigir Erros,9,Corrupção,1,CPU,2,Crackers,2,Crackle,1,CreativeCopias,4,CriaDEB,1,Criptografia,4,CrunchBang,1,CSS,1,Cub Linux,1,Cuba,1,CUPS,3,Curlew,1,Curso em Vídeo,2,Cyanogen,2,CyanogenMod,2,CyanogenMod 14.1,1,Damn Vulnerable Linux,1,Darktable,1,DBeaver,1,DD,2,DDoS,6,Ddrescue-GUI,2,DeadBeef,1,DEB,7,Debian,144,Debian 8,1,Debian 9,5,Debian 9 Stretch,2,Debian Stretch,2,Debian Xbluebuntu,1,Deepin Software Center,1,Deficientes,1,DEFT Linux,1,Delaboratory,1,Dell,1,Derek Banas,1,Descompactação,3,Desempenho,2,Desenvolvimento,15,Desktop,3,Detox,1,Deus Ex,2,Dev,1,Device Driver Manager,1,Devuan,1,DHCP,1,DIA,1,Dicas,509,Dilma,1,Directx,1,Dirty COW,2,Disco Rígido,2,Disney,2,Distribuições Linux,28,DJ,1,Django,1,dmicode,1,Dnf,1,DNS,3,Docker,3,DOS,1,Downloads,4,dpkg,1,DragonBoard 410c,1,Drivers,7,Drones,1,Dropbox,1,Dual Boot,1,Duzeru,1,Dyn,2,Easyimagesizer,1,eBook,3,Economia Bateria,1,Edge,1,Editor de áudio,1,Editor de código,3,Editor de Fontes,1,Editor de imagens,11,Editor de Legendas,1,Editor de Vídeo,3,Edubuntu,1,EducatuX,1,Edward Snowden,7,Efeitos,1,Elemental,1,ElementaryOS,25,Elive,1,ELRepo,1,Embarcados,1,Emby,1,Emmabuntüs,3,emmbx,1,Empacotamento,2,Emprego,5,Emuladores,2,End Of The Mine,1,Endian Firewall,1,Endless,4,Engenharia Reversa,1,Engenharia Social,1,Enlightenment,1,Enpass,1,Entangle,1,Epic Games,1,Epidemic,2,Erros,1,eSpeak,1,Espionagem,2,Estados Unidos,1,Etsy,1,EUA,8,Eventos,11,eXcript,1,ExTiX,1,ExTiX 16.5,1,Exton,1,Exton|Defender SRS,1,Exton|OS,1,Exton|OS Light Build 161021,1,Facebook,8,Facebook Messenger,2,FBI,2,Fedora,100,Fedora 23,1,Fedora 24,4,Fedora 25,8,Fedora 25 Beta,1,Fedora Game Spin,1,Fedora Spins,1,Feed,1,Feral Interactive,2,FFmpeg,2,FileZilla,3,Filmes,1,Firefox,10,Firefox 50.0.2,1,Firefox Developer Edition,1,FirefoxOS,1,Firewall,3,Firmware,1,Fish,1,FISL,5,Flash,1,Flash Player,1,Flatpak,1,FlightGear,1,Flowblade,1,Fluxbox,1,FocusWritter,1,Fontes Microsoft,1,Fora do ar,2,Foremost,1,Forense,1,Fortress Legends,1,FOSS,3,Fotos,1,Foxit Reader,2,Fractgen,1,Franquia,1,FreeBSD,7,FreeBSD 11.0,1,FreeCAD,1,Freenas,2,Fritzing,1,Frontend,1,FrostWire,2,Frugalware,1,FSearch,1,FSF,8,FSlint,1,FTP,4,Fuchsia,2,Fujitso,1,Funcionamento,1,Gameplay,3,Games,39,Gazebo,1,GCC,1,GCC 5.3.0,2,Geary,1,Geary Mail,1,Gentoo,3,Gerenciador de Download,2,Gespeaker,1,gFTP,2,Ghost Push,1,GhostBSD,1,Giada,1,Gimp,6,Git,2,Git 2.8.4,1,GitHub,3,Gmail,2,GNOME,13,Gnome 3,1,GNOME 3.22,3,GNOME 3.22.1,1,GNOME 3.22.2,1,GNOME 3.23.1,1,GNOME 3.24,1,Gnome Shell Elegance Colors,1,GNU,6,GNU/Hurd,1,GNU/Linux,564,GNU/Linux-libre,4,Go,2,GoDaddy,1,Goobuntu,1,Google,30,Google Calendar Indicator,1,Google Chrome,8,Google Chrome 54,1,Google Chrome 55,2,Google Chromium,1,Google Home,1,Google Pixel,2,Google Pixel XL,2,Google Play,1,Gooligan,1,GParted,3,GPast,1,GPL,1,GPS,1,Gravadores,2,Greenbone,1,Groovy,1,GRUB,4,Grub Customizer,2,Grupo Telegram,1,GSCAN2PDF,2,GSoC,1,GTK,2,gtkdialog,1,Guias,5,GuixSD,1,Hackers,26,Hacking,4,Haguichi,1,Hamachi,3,HandyLinux,1,Hannah Montana,1,HardenedBSD,1,Hardware,3,Hardware Livre,2,Haters,1,Hawaii,1,HDD,1,HDRMerge,1,Heartbleed,1,Heindall,1,Hexy,1,Hibernar,1,Honeytraps,1,Hospedagem,1,hostname,1,Hotshots,1,HP,6,HPLIP,4,HTML,1,HTML5,1,Humble Bundle,1,Hydra,1,IBM,4,ícones,1,ICReach,1,ICS SIEM,1,IDE,1,Ifconfig,2,IFTO,1,IFTTT,1,ImageGate,1,ImagePlay,1,Impressoras,10,Indicator Netspeed,1,Inicialização do sistema,1,Inkscape,3,Instalar,3,Integridade,1,Intel,5,Intel Connected,1,Intel Graphics Installer,1,Inteligência Artificial,1,Internet,2,Internet das coisas,3,Internet Explorer,1,Inxi,1,iOS,4,iOS 10,1,IP,1,IPFire,1,iPhone,2,Iptables,1,IPTraf,1,Ipv4,1,Ipv6,2,iRobot Create,1,ISO,2,ISO 8859-1,1,iXSystem,1,JASmine,1,Java,5,Java Oracle,2,JavaScript,1,Jerry Bezencon,1,Jitsi,2,JMicron JMC250,1,Jogos,12,John McAfee,1,John The Ripper,1,Jono Bacon,1,Joomla,5,JS-01,1,Juliette Taka Belin,1,JWM,1,Kaiana,2,Kakoune,1,Kali Linux,2,KaOS,3,KaOS 2016.11,2,Karma,1,Kate,1,KDE,21,KDE Connect,1,Kde neon,1,KDE Plasma,6,KDE Plasma 5.10,1,KDE Plasma 5.11,1,KDE Plasma 5.12,1,KDE Plasma 5.13,1,KDE Plasma 5.14,1,KDE Plasma 5.7,1,KDE Plasma 5.8,3,KDE Plasma 5.8 LTS,1,KDE Plasma 5.9,1,KDE4,1,kdenlive,1,KeePassX,1,Kernel,110,Kernel Linux,50,Kernel Linux 4.8,3,kernel linux 4.8.5,1,kernel linux 4.8.6,2,kernel linux 4.8.8,1,Kernel Linux 4.8.9,1,Kernel Linux 4.9-rc6,1,kernel Linux 4.4,15,kernel Linux 4.4.19,1,kernel Linux 4.4.28.,1,kernel linux 4.4.29,2,kernel linux 4.4.30,2,Kernel Linux 4.4.31,1,Kernel Linux 4.4.32,2,Kernel Linux 4.4.34,3,Kernel Linux 4.4.35,1,Kernel Linux 4.4.36,1,Kernel Linux 4.7,1,kernel linux 4.8,5,Kernel Linux 4.8.10,2,Kernel Linux 4.8.11,2,Kernel Linux 4.8.12,1,kernel Linux 4.8.6,1,kernel Linux 4.8.7,2,Kernel Linux 4.8.8,2,kernel linux 4.9,5,Kernel Linux 4.9-rc4,1,Kernel Linux 4.9-rc5,1,Kernel Linux 4.9-rc7,1,Kernel Linux 4.9-rc8,1,Kernel Linux-Libre 4.4,1,KID3,1,Kodi,2,KolibriOS,1,Komodo Edit,1,Komodo Edit 10.1,1,Kotlin,1,Krita,1,Krop,1,Kryptowire,1,Ksplice,1,Kubuntu,3,Kubuntu 16.10,1,LAMP,1,Lançamento.,2,Launchpad,1,Leap Day,1,Leitor eBook,1,LeJOS,1,Lenovo,3,Lentes Amazon,1,Let's Encrypt,1,LGBT,3,Liberdade,1,Libre,1,Libreboot,3,LibreCAD,3,LibreOffice,23,LibreOffice 5.1,1,LibreOffice 5.2,1,LibreOffice 5.2.3,1,LibreOffice 5.3,4,LibreOffice 5.3.0,1,LibreOffice 5.3.0 Beta,1,LibreOffice Calc,1,LibreOffice Kit Plus,1,Liferea,1,Lightbeam,1,LightDM,1,Lightworks,3,Lili USB,1,LinConnect,1,Linguagem C,1,LinkedIn,2,Linphone,1,Linus Torvalds,9,Linux,180,Linux AIO,1,Linux Foundation,6,Linux Journey,1,Linux Kernel,35,Linux Kernel 4.4.31,1,Linux Kernel 4.4.32,2,Linux Kernel 4.4.33,1,Linux Kernel 4.8,1,linux kernel 4.8.7,1,Linux Kernel 4.8.8,2,Linux Kernel 4.8.9,1,Linux Lite,2,Linux Lite 3.2,1,Linux Mint,136,Linux Mint 18.1,5,Linux Mint 18.1 Serena,1,Linux-Libre,1,LinuxConsole,1,linuxcounter,1,Live CD,1,Livre Labs,2,Livros,2,LM18,1,Loiane Groner,1,Loja,2,Lollypop,1,Looke,1,LPI,5,LUA,2,Lubuntu,5,Lubuntu 16.10,1,Lucidor,1,Lumina,1,LVM,2,LXLE,1,LXQT,1,Mac,4,Mac Address,1,Mac OS X,4,MacBook,1,MacBook Pro,1,Macbuntu Transformation Pack,1,MacOS X,13,MacOs-Linux,1,MacUbuntu,2,Mad Max,2,MADRUGUEDS,1,Madruguedsbr,9,Mageia,21,Mageia 5.1,1,Magic Device Tool,1,magic-device-tool,2,Mala Direta,1,Malware,8,Manjaro,15,Manjaro 16.10,2,Manjaro 16.10 Fringilla,2,Manjaro 16.10.2,1,Manjaro Fringilla,2,Manjaro Linux Gaming 16.06,1,Mantra-OS,1,Manuais,2,Marius Quabeck,1,Maru OS,3,MaruOS,2,Mate,1,Maui Linux,2,McAfee,1,MDM,1,Meadow,1,Meizu PRO 5 Ubuntu Edition,1,Memristor,1,Mensageiro,2,Mercado,1,Messenger,1,Microsoft,26,Microsoft Azure,1,Microsoft Silverlight,2,Microsoft Solitaire Collection,1,Microsoft Surface Studio,2,Midori,1,MightyText,1,Migração Windows para Linux,1,Min Browser,1,MiniTube,1,Minix,2,Mirai,1,Mixxx,1,MKV,2,MKVToolnix,1,Moksha,1,Moksha 0.2.1,1,Monitoramento de rede,1,Moon Buggy,1,MORSE,1,Mouse,1,Mozilla,10,Mozilla Firefox,1,Mozilla Firefox 50.0.2,1,MRTG,1,MSLinux,1,Multiload Indicator,1,Músicas,1,Musique,1,Mycroft,3,Mysql,3,Mythbuntu,1,Mythbuntu Linux,1,MythTV,4,NAS,1,NASA,1,Navegadores,11,NeocoreGames,1,Neptune,2,Nessus,1,Netbook,1,NetBSD,1,NetBSD Project,1,Netflix,1,Netflix Desktop,1,Nethack,1,NetKit,1,NetMovies,1,Netrunner,1,Netrunner Desktop,1,Netrunner Desktop 16.09,1,Netrunner Desktop 16.09 Avalon,1,Network Manager,1,NetworkMiner,2,Neverball,1,New York Times,1,Ninvaders,1,Nmap,5,NodeJS,2,Nomacs,1,Nootka,1,nOS,1,Notebook,4,Notepad,1,Notepadqq,1,Notícias,564,NotifyOSD,1,Novidades,1,NSA,14,NST,1,Nudoku,1,Num Lock,1,Nvidia,7,Nylas N1,1,Objective-C,1,Ochdownloader,1,ODF,1,Office,1,OGMRip,1,Oldflix,1,Omega2,1,OmniROM,1,OneOps,1,OnlyOffice,1,Open Broadcaster,1,Open Source,24,Open Source Robotics Foundation,1,Open365,2,Openbox,1,OpenBSD,2,OpenDesk,1,openMandriva,11,OpenOffice,1,OpenPilot,1,OpenPrinting,1,OpenSCAD,1,OpenShot,1,OpenSSH,1,openSUSE,86,openSUSE 42.2,3,openSUSE Factory,1,openSUSE Leap,4,openSUSE Leap 42.2,4,openSUSE Tumbleweed,2,OpenSwitch,1,OpenTTD,1,OpenVas,1,Opera,4,Opera 40,1,Opera 41,1,Opera Browser,5,Opinião,19,Oracle,2,Oranchelo,1,Organizadores,1,Orocos,1,OsoLinux,1,Otimizações,2,Outfolded,1,OWASP,1,owncloud,2,pacman,3,Pacotes,4,Paintown,1,Pale Moon,2,pandoc,1,Papel de parede,2,Paperhouses,1,Parabola,1,Paramount Pictures,1,Parrot Security 3.2,1,Parrot Security OS,3,Parsix,1,Parted Magic,1,Parted Magic 2016_10_18,1,PC-BSD,1,PCLinuxOS,1,PDC,1,PDF,10,PearOS,1,PeaZip,2,Pen Drive,2,Pendrive,3,Pentest,3,Pentoo,2,PeppermintOS,2,Perl,1,Personalização,1,Perspectiva,1,Pesquisa,1,pfSense,2,Phoenix Os,2,Photoqt,1,PHP,2,Picapy,1,Picasa,1,PicasaWeb,1,Pidora,1,Pipe,1,Pipelight,3,Pirataria,2,Pitivi,1,Plank,1,Plasma,1,Play Linux,1,Play Store,1,Player,5,PlayOnLinux,2,PlayStation,2,Plugins,1,Point Linux,1,Poison Tap,1,Pokémon GO,1,Poker,1,PokerTH,1,Polkast,1,Porteus Kiosk,1,Poseidon Linux,1,PotiCon,1,PotiLivre,1,PowerPC,1,PowerPoint,1,PowerShell,3,PPA,43,PPSSPP,1,Pré-FISL,1,Prelink,1,Preload,2,Prioridades,1,Privacidade,6,Processadores,3,Processos,1,Produtividade,3,Profile Sync,1,Profissional de informática,1,Proftpd,2,Programação,23,Programação Funcional,1,ProgrammingKnowledge,1,Project CARS,1,ProjectLibre,1,Projeto Fedora,3,Projetos,3,Prozilla,1,Pushbullet,1,Puzzle,1,Python,12,Python 3,1,Q4OS,2,QCAD,2,QEMU,2,QGifer,1,QGIS.,1,Qt,3,Quelitu,1,quoted,1,QupZilla,2,R.,1,RacnherOS,1,RadarCat,1,Ragentek Group,1,RAID,1,RAM,2,Ransomware,4,Raspberry Pi 2,2,Raspberry Pi 3,1,RaspberryPi,10,RaspberryPi Zero,2,RaspberryPi2,4,RaspberryPi3,3,Raspbian,2,RaspEX,1,RaspEX 161019,1,RaspEX Build 161019,1,RawTherapee,1,RazorSQL,2,ReactOS,3,ReactOS 0.4.3,2,Rebellin,1,Recife,1,Recuperação de dados,5,Red Hat,11,Red Hat Enterprise Linux,3,Red Hat Enterprise Linux 7.3,1,Redes,2,Reflector,1,Relatórios,1,Release Candidate,5,Release Candidate 2,1,Relógio,1,Remasters,1,Remix OS,3,RemoteBox,2,repositórios,1,Requisitos,1,Rescatux,1,Reset,1,Restauração do sistema,1,Review,17,Rexloader,1,RHEL,2,Rhythmbox,1,Richard Stallman,3,Rigs of Rogs,1,Ripadores,1,ROADSEC,1,Robô,5,Robot Finds Kitten,1,Robótica,1,Rock,1,Rocket League,1,Rolling Release,1,Rolling Sky,1,ROM,1,ROOT,2,ROS,1,Rosa,1,Rosa Linux,3,RPM,9,RSS,1,Ruby,1,Rule41,1,Rússia,1,Rust,1,Sabayon,2,Sabayon 16.11,1,Safe RM,1,SafeEyes,1,Sailfish OS,2,SalentOS,1,Salix,4,Salix Xfce 14.2,1,Salix Xfce 14.2 GNU/Linux,1,Salix Xfce 14.2 Live Edition RC1,1,Samba,6,Samsung,3,Samsung Modelo M2070W,1,Samy Kamkar,1,Sarg,2,ScreenSaver,1,Script,2,SDK,1,Segurança,72,SELKS,1,SempreUPdate,11,Senhas,3,Sensor,1,Serena,2,Séries,1,Servidores,4,Shanghai Adups Technology,1,SHAREit,1,Shell,4,ShellScript,4,Shellshock,1,Shopify,1,Shotcut,1,SimpleScreenRecorder,3,SimplicityLinux,1,Simuladores,4,SIS,1,Sistema Operacional,35,Sistema Telefônico,1,Sistemas Embarcados,1,Site,2,Sitecake,1,Six!,1,Skype,5,Skype 1.13,1,Slackware,5,Slackware 14.2,2,Slayaway Camp,1,slocate,1,SlowmoVideo,1,Smart TV,1,Smartphone,8,Smilla Enlarger,1,Snappy,1,SniffJoke,1,Software,2,Software Livre,24,Software Proprietário,3,Solus,3,Solus Mate,1,SolveSpace,1,Sonar,1,Sony,2,Sparki,1,Sparky Linux,1,SparkyLinux,2,SparkyLinux GameOver,1,Spotify,1,SQL Server,1,Squid,3,Stanford,1,Star Wars,1,STD,1,Steam,8,Steam Link,1,SteamOS,5,SteamOS 2.0,1,Stellarium,1,Stellarium 0.15,1,Stellarium 0.15.0,1,Stoq,1,Streaming,1,StreamStudio,1,Stretch,1,String,1,sudo,2,Suíte de Escritório,1,Superaquecimento,1,Superb Mini Server,1,SuperBeam,1,SuperTux,1,SuperTuxKart,1,Surface Book,1,Surface Studio,2,Surface Studio i7,1,SUSE,1,SUSE Studio,1,Sweet Home 3D,1,Swift,2,SymphonyOS,1,Synaptic,1,Syntax error,1,Systemback,1,SystemD,1,T50,1,TAE,1,Tails,6,Tails 2.7,1,Tails 3.0,1,Tails 3.0~alpha1,1,TCC,2,TDF,1,TeamViewer,2,Telegram,6,Temas,7,Terminal,7,Testes de penetração,2,The Document Foundation,1,The Linux Foundation,2,Thermald,1,Thunderbird,1,TimeKpr,1,Timeline,1,TimeShift,1,Tiny Core,1,TinyCore,1,Tixati,1,Tizen,1,TLP,1,TODO,1,TOMAHAWK,1,Toners,2,TOR,4,Tor Browser,12,Tor Messenger,1,Tor Onion,1,Torrent,5,Touchpad Indicator,1,TPP,1,Travamentos,1,Treehouse,1,Trojan,3,Troubleshooting,1,TrueOS,1,Tumbleweed,1,Turnkey Linux,1,Turtlebot,1,Tutoriais,116,TuxMath,1,TV,3,TV-Maxe,2,Twitter,2,UberStudent,1,Ubuntu,337,Ubuntu 12.04,2,Ubuntu 14.04,2,Ubuntu 16.04,21,Ubuntu 16.04.1,1,Ubuntu 16.10,18,Ubuntu 17.04,2,Ubuntu After Install,2,Ubuntu Budgie,1,Ubuntu Budgie Remix,2,Ubuntu Budgie Remix 16.10,1,Ubuntu Christian Edition,1,Ubuntu Core,1,Ubuntu DesktopPack,1,Ubuntu Dual Boot Touch,1,Ubuntu Edge,1,Ubuntu GamePack,1,Ubuntu Gnome,5,Ubuntu GNOME 16.10,1,Ubuntu Kylin,1,Ubuntu Mate,2,Ubuntu Mate 16.04,1,Ubuntu MATE 16.10,2,Ubuntu Satanic Edition,1,Ubuntu Server,2,Ubuntu Snap,9,Ubuntu Snappy Core,1,Ubuntu Snappy Core 16,1,Ubuntu Touch,7,Ubuntu TV,1,ubuntu-xboxdrv,1,UbuntuBSD,2,Udisks Indicator,1,Unity,11,Unity 8,2,Univention Corporate Server,1,Universidade,1,Unix,4,Unsettings,1,Upgrade,4,USB KILL,1,UTF8,1,uTorrent,6,V-REP,1,Valve,1,Variety,1,VBA-M,1,VeltOS,1,Vendetta,1,Verizon,3,Viber,2,Vídeos,68,Videos On Demand,1,Vigilância,1,Vim,2,Vim 8.0,1,Vindows,1,Vindows Locker,1,Vine Linux,1,Virtual Box Guest Addons,1,VirtualBox,4,Vírus,1,Visual Basic .NET,1,VivaoLinux,1,VLC,2,VLC 360,1,VMware,4,VMware Player,2,VoIP,1,Volatility Framework,1,Voyager GNU/Linux,1,VPN,2,VSIDO,1,VueScan,1,Vulkan,1,Vuze,1,w3af,1,Wallch,1,Walmart,1,wattOS,2,Web,2,WebCam Studio,1,Webconverger,1,Webmin,1,WebODF,1,Western Digital,1,Wget,2,Whatsapp,1,WHDD,1,Whisker Menu,1,White Noise 2,1,Wi-Fi,3,Wifi,6,Wifislax,1,Wikihouse,1,Wikileaks,3,Windows,37,Windows 10,2,Windows 2000,1,Windows 2003,1,Windows NT,1,Windows NT4,1,Windows Phone,1,Windows XP,1,Windows-TuneUp,1,Wine,8,Wine 1.9.23,1,Wine Staging,1,Wireshark,2,Worms,1,Xbox,2,XDM,1,Xenial Xerus,1,Xfce,9,Xfdashboard,1,XiX,1,XnConvert,3,XOR DDoS,1,xpad,1,Xplanetfx,1,XScreenSaver,1,Xtreme Download Manager,1,Xubuntu,14,Xubuntu 16.10,1,Y PPA Manager,3,Yahoo,4,Yahoo Mail,4,Yakkety Yak,4,Yandex Browser,1,yaourt,1,Yout,1,YouTube,3,Zabbix,1,Zbackup,1,Zbigniew Konojacki,1,Zentyal,1,Zenwalk,1,Zesty Zapus,2,ZevenOS,1,ZFS,1,Zimperium,1,Zorin Desktop 2.0,1,Zorin OS,1,Zorin OS 12,1,Zotero,1,Zram,1,
ltr
item
SempreUPdate: Como acessar o PostgreSQL com C – Cursores
Como acessar o PostgreSQL com C – Cursores
Acessando o PostgreSQL com C
https://2.bp.blogspot.com/-lnxtuoEy07I/V0ZdnwUSIUI/AAAAAAAAWNI/uDP2HTRfhl85E19rEQq7D1bBrWPq27boACLcB/s640/PostgreSQL.png
https://2.bp.blogspot.com/-lnxtuoEy07I/V0ZdnwUSIUI/AAAAAAAAWNI/uDP2HTRfhl85E19rEQq7D1bBrWPq27boACLcB/s72-c/PostgreSQL.png
SempreUPdate
http://www.sempreupdate.com.br/2016/05/como-acessar-o-postgresql-com-c-cursores.html
http://www.sempreupdate.com.br/
http://www.sempreupdate.com.br/
http://www.sempreupdate.com.br/2016/05/como-acessar-o-postgresql-com-c-cursores.html
true
4871636227574760232
UTF-8
Carregar todas as postagens Não encontrado nenhum post VER TUDO Leia Mais Responder Cancelar Resposta Apagar Por Home PÁGINAS POSTAGENS Ver tudo RECOMENDADOS PARA VOCÊ MARCADOR ARQUIVO BUSCAR TODOS AS POSTAGENS Não encontramos nada com a palavra digitada Voltar para Home Domingo Segunda-feira Terça-feira Quarta-feira Quinta-feira Sexta-feira Sábado Dom Seg Ter Qua Qui Sex Sab Janeiro Fevereiro Março Abril Maio Junho Julho Agosto Setembro Outubro Novembro Dezembro Jan Fev Mar Abr Maio Jun Jul Ago Set Out Nov Dez Agora mesmo 1 minuto atrás $$1$$ minutes ago 1 hora atrás $$1$$ hours ago Ontem $$1$$ days ago $$1$$ weeks ago mais de 5 meses atrás Seguidores Seguir ESTE CONTEÚDO É PREMIUM Por favor, compartilhe para desbloquear Clique aqui para copiar o código Clique aqui para copiar o código Código copiado para área de transferência Não é possível copiar os códigos/textos, por favor pressione [CTRL] + [C] (ou CMD + C no Mac) para copiar