Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

_Isis_

[Tutorial] Banco de Dados em C

Recommended Posts

Vou linkar p/ o blog, porque tentei colar isso ontem e foram-se todas as quebras de linha. http://strufts.blogs...0/sqlite-c.html Talvez a segunda parte seja com MySQL. E vai ser assim mesmo, bem básico, porque documentação existe no site e como sempre, o problema é começar. Edit: na verdade não importa o que eu digite na caixa de texto. Todas as quebras de linha somem.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Algumas pessoas param no iMasters e acabam perguntando como usar C com algum header de banco de dados. Resolvi escrever um tutorial simples abordando Sqlite3. Lembrando que uso Linux p/ desenvolver em C. Não postei lá porque as quebras de linha somem.

 

Normalmente os trabalhos de faculdade pedem que você utilize um arquivo texto no estilo CSV, seja com vírgula ou ponto-e-vírgula como separador do campo. Cabe a você ler a linha do arquivo e separar na mão os campos antes de atribuir a uma struct. Fazer isso é válido até 2 vezes, mas depois é desperdício de tempo, já que você simplesmente troca o token (e alguns problemas também), ao invés de utilizar banco de dados e aproveitar p/ aprender algo novo ou exercitar o que se vê na matéria de BD. Fato: aplicações sérias usam banco de dados.

 

 

SQLITE3

 

http://www.sqlite.org/

 

A maioria dos bancos de dados possuem um daemon/servidor que é executado em background, aceitando requisições dos programas. O Sqlite é exceção. Não é necessário iniciar um servidor p/ que você possa utilizar o banco. Eis um arquivo de configuração do Django de um projeto meu:

 

DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = PROJECT_DIR + '/db/pimp_my_gedit.db' # Or path to database file if using sqlite3.
DATABASE_USER = '' # Not used with sqlite3.
DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.

Não há usuário, senha, host e nem porta envolvidos. E sim, o banco de dados é um arquivo.

 

Precisamos instalar o pacote sqlite3-devel. Após a instalação, veja que ele se encontra em /usr/include/. Vamos criar um banco de dados simples:

 

PAIS:

- ID

- SIGLA (CHAR 2, ÚNICO)

 

MONTADORA:

- ID

- NOME (CHAR 30, ÚNICO)

- PAÍS (CHAVE ESTRANGEIRA, REFERENCIA PAIS.ID)

 

 

AUTOMÓVEL:

- ID

- NOME (CHAR 30)

- ANO (INT)

- FABRICANTE (CHAVE ESTRANGEIRA, REFERENCIA MONTADORA.ID)

Para criar o banco de dados usamos o comando sqlite3 exemplo_carro.db e então entramos na sessão interativa, de onde iremos criar as tabelas. Se você quiser visualizar os bancos de dados criados durante a sessão, digite .databases (todos os comandos do sqlite3 iniciam com um ponto-final). Os formatos dos comandos suportados pelo Sqlite3 estão no site oficial.

 

sqlite> create table main.pais(id INTEGER PRIMARY KEY, sigla CHAR(2) UNIQUE);
sqlite> create table main.montadora(id INTEGER PRIMARY KEY, nome CHAR(30) UNIQUE, origem REFERENCES pais(id));
sqlite> create table main.automovel(id INTEGER PRIMARY KEY, nome CHAR(30), ano INTEGER, fabricante REFERENCES montadora(id));
O "main" antes de cada tabela especifica em qual banco de dados ela será criada. Quando se usa o comando .databases, na segunda coluna aparece o nome do banco de dados utilizado. NÃO SE USA O NOME DO ARQUIVO NO SISTEMA DE ARQUIVOS.

 

 

Iremos agora escrever um programa em C que popula as tabelas com os seguintes dados:

 

PAIS:

(1, FR) , (2, DE), (3, IT)

 

 

MONTADORA:

(1, RENAULT, 1), (2, VOLKSWAGEN, 2), (3, FIAT, 3)

 

 

AUTOMÓVEL

(1, MEGANE, 2009, 1)

Para acessar o banco de dados é necessário abrir uma conexão. Isso é feito através da função sqlite3_open_v2 (API nova), que aceita quatro argumentos:

 

1- Nome do arquivo onde foram criadas as tabelas.

2- O ponteiro da conexão sqlite3.

3- Indicadores de modo de operação (SQLITE_OPEN_READWRITE, SQLITE_OPEN_READONLY, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE)

4- Ponteiro para o objeto que define a interface a ser usada.

 

Como existem vários códigos de erros e não é uma boa prática tentar adivinhar o que ocorre com mais frequência e escrever uma mensagem diretamente no código, vamos usar a função sqlite3_errmsg, que aceita o objeto sqlite3 e retorna uma string com a mensagem correspondente ao erro.

 

#include <stdio.h>
#include <sqlite3.h>
#include <errno.h>
int main() {

const char *arquivo = "exemplo_carro.db";
sqlite3 *obj_conexao;

if (!obj_conexao) {
printf("%m\n");
return 1;
}

int status = sqlite3_open_v2(arquivo, &obj_conexao, SQLITE_OPEN_READWRITE, NULL); // Utiliza o objeto sqlite3_vfs padrão.

if (status != SQLITE_OK) {
printf("Erro ao abrir conexão: \n\t%s\n", sqlite3_errmsg(obj_conexao));
return 1;
}

status = sqlite3_close(obj_conexao);
return 0;
}
Para compilar o programa temos que linkar explicitamente a biblioteca (-lsqlite3). Nota: se tentar utilizar o malloc o programa não compila. Agora iremos popular as tabelas. Para isso utilizamo a função sqlite3_exec.

 

#include <stdio.h>
#include <string.h>
#include <sqlite3.h>
#include <errno.h>

void popular_tabela_pais(sqlite3 *obj_conexao) {

const char *SQL_STATEMENT[] = { "insert into pais values (1, 'FR');", "insert into pais values (2, 'DE');", "insert into pais values (3, 'IT');" };
int i;
int status;
char * errplace;

for (i = 0; i < 3; i++) {
status = sqlite3_exec(obj_conexao, SQL_STATEMENT[i], NULL, NULL, &errplace);

if (status != SQLITE_OK) {
printf("Ocorreu um erro ao executar a consulta \" %s \"\n", SQL_STATEMENT[i]);
printf("Erro: \n\t %s\n", errplace);
}
}

}


int main() {

const char *arquivo = "exemplo_carro.db";
sqlite3 *obj_conexao;


int status = sqlite3_open_v2(arquivo, &obj_conexao, SQLITE_OPEN_READWRITE, NULL); // Utiliza o objeto sqlite3_vfs padrão.

if (status != SQLITE_OK) {
printf("Erro ao abrir conexão: \n\t%s\n", sqlite3_errmsg(obj_conexao));
return 1;
}


popular_tabela_pais(obj_conexao);


status = sqlite3_close(obj_conexao);
return 0;
}
Antes de executar o programa, a tabela pais está vazia:

 

isis@linux-45c9:~/src> sqlite3 exemplo_carro.db
SQLite version 3.6.4
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .databases
seq name file
--- --------------- ----------------------------------------------------------
0 main /home/isis/src/exemplo_carro.db
sqlite> .tables
automovel montadora pais
sqlite> select * from pais;
sqlite>
sqlite> .quit
isis@linux-45c9:~/src> ./a.out
isis@linux-45c9:~/src> sqlite3 exemplo_carro.db
SQLite version 3.6.4
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> select * from pais;
1|FR
2|DE
3|IT
sqlite> .quit
Tente executar o programa novamente e você verá 3 mensagens de erro, que indicam a impossibilidade de adicionar as tuplas por causa da restrição no campo ID, que deve ser único. Podemos verificar que outras mensagens são exibidas alterando o comando SQL: se o campo ID estiver vazio, o programa exibe "datatype mismatch". Abaixo está o código de inserção dos outros valores.

 

void popular_tabela_montadora(sqlite3 *obj_conexao) {
const char *SQL_STATEMENT[] = { "insert into montadora values(1, 'RENAULT', 1);",
"insert into montadora values(2, 'VOLKSWAGEN', 2);",
"insert into montadora values(3, 'FIAT', 3);"};
int i;
int status;
char * errplace;

for (i = 0; i < 3; i++) {
status = sqlite3_exec(obj_conexao, SQL_STATEMENT[i], NULL, NULL, &errplace);

if (status != SQLITE_OK) {
printf("Ocorreu um erro ao executar a consulta \" %s \"\n", SQL_STATEMENT[i]);
printf("Erro: \n\t %s\n", errplace);
}
}

}


void popular_tabela_automovel(sqlite3 *obj_conexao) {
const char *SQL_STATEMENT[] = { "insert into automovel values(1, 'MEGANE', 2009, 1);"};
int i;
int status;
char * errplace;

for (i = 0; i < 1; i++) {
status = sqlite3_exec(obj_conexao, SQL_STATEMENT[i], NULL, NULL, &errplace);

if (status != SQLITE_OK) {
printf("Ocorreu um erro ao executar a consulta \" %s \"\n", SQL_STATEMENT[i]);
printf("Erro: \n\t %s\n", errplace);
}
}
}
Note que as funções diferem apenas no SQL, então, para não escrevermos a mesma coisa a cada tabela que for preciso criar, poderíamos usar 3 arquivos XML ou CSV, até mesmo reescrever as funções. O programa abaixo obtém todas as tuplas da tabela MONTADORA.

 

#include <stdio.h>
#include <string.h>
#include <sqlite3.h>
#include <errno.h>

int main() {

const char *arquivo = "exemplo_carro.db";
sqlite3 *obj_conexao;


int status = sqlite3_open_v2(arquivo, &obj_conexao, SQLITE_OPEN_READWRITE, NULL); // Utiliza o objeto sqlite3_vfs padrão.

if (status != SQLITE_OK) {
printf("Erro ao abrir conexão: \n\t%s\n", sqlite3_errmsg(obj_conexao));
return 1;
}


const char * SQL_STATEMENT = "SELECT * FROM MONTADORA;";
char * errplace;

status = sqlite3_exec(obj_conexao, SQL_STATEMENT, NULL, NULL, &errplace);
if (status != SQLITE_OK) {
printf("Ocorreu um erro ao executar a consulta \" %s \"\n", SQL_STATEMENT);
printf("Erro: \n\t %s\n", errplace);
} else {
puts("Comando executado.");
}

status = sqlite3_close(obj_conexao);
return 0;
}
Note que não foi exibido nada na tela. Para operar sobre os resultados utilizamos o terceiro parâmetro da função sqlite3_exec: um ponteiro para uma função com 4 argumentos:

 

1- void*

2- int : número de tuplas que satisfazem a consulta

3- char** : valor do campo da tupla

4- char** : nome da coluna da tupla

 

int imprimir_resultados(void * ptr, int resultados, char ** STR1, char **STR2) {
printf("Resultados encontrados: %d\n", resultados);
printf("%s = %s\n", *STR2, *STR1);
return 0;
}
Com a função acima só imprimimos a primeira coluna das tabelas, o ID. Se quisermos imprimir o restante das colunas, temos que verificar se o valor de STR1 não é NULL.

 

int imprimir_resultados(void * ptr, int resultados, char ** STR1, char **STR2) {
int i;

for(i = 0; STR1[i] != NULL; i++)
printf("%s = %s\n", STR2[i], STR1[i]);

return 0;
}
Aqui cabe um aviso: oriente-se pelo primeiro char**. Veja a saída do GDB:

 

(gdb) p STR1[0]
$1 = 0x80588d8 "1"
(gdb) p STR1[1]
$2 = 0x8058940 "MEGANE"
(gdb) p STR1[2]
$3 = 0x8058870 "2009"
(gdb) p STR1[3]
$4 = 0x80583f8 "1"
(gdb) p STR1[4]
$5 = 0x0
(gdb) p STR2[0]
$6 = 0x8058a10 "id"
(gdb) p STR2[1]
$7 = 0x8058188 "nome"
(gdb) p STR2[2]
$8 = 0x8057d10 "ano"
(gdb) p STR2[3]
$9 = 0x8057eb0 "fabricante"
(gdb) p STR2[4]
$10 = 0x80588d8 "1"
(gdb) p STR2[5]
$11 = 0x8058940 "MEGANE"
(gdb) p STR2[6]
$12 = 0x8058870 "2009"
(gdb) p STR2[7]
$13 = 0x80583f8 "1"
(gdb) p STR2[8]
$14 = 0x0
(gdb) p STR2[9]
$15 = 0x0

É isso: o segundo char** passado para a função não possui NULL p/ delimitar o número de colunas, "retornando" para os valores das tuplas. Eis um exemplo de programa que recupera os dados relativos a um automóvel.

 

#include <stdio.h>
#include <string.h>
#include <sqlite3.h>
#include <errno.h>


int imprimir_resultados(void * ptr, int resultados, char ** STR1, char **STR2) {
int i;

for(i = 0; STR1[i] != NULL; i++) {
printf("%s = %s\n", STR2[i], STR1[i]);
}
return 0;
}


int main() {

const char *arquivo = "exemplo_carro.db";
sqlite3 *obj_conexao;


int status = sqlite3_open_v2(arquivo, &obj_conexao, SQLITE_OPEN_READWRITE, NULL); // Utiliza o objeto sqlite3_vfs padrão.

if (status != SQLITE_OK) {
printf("Erro ao abrir conexão: \n\t%s\n", sqlite3_errmsg(obj_conexao));
return 1;
}


const char * SQL_STATEMENT = "select automovel.nome, automovel.ano, tb1nome, tb1pais from (select pais.sigla as tb1pais,montadora.nome as tb1nome ,montadora.id as mid from pais inner join montadora on montadora.origem = pais.id) as tb1 inner join automovel on automovel.fabricante = tb1.mid;";
char * errplace;

status = sqlite3_exec(obj_conexao, SQL_STATEMENT, imprimir_resultados, NULL, &errplace);
if (status != SQLITE_OK) {
printf("Ocorreu um erro ao executar a consulta \" %s \"\n", SQL_STATEMENT);
printf("Erro: \n\t %s\n", errplace);
} else {
puts("Comando executado.");
}

status = sqlite3_close(obj_conexao);
return 0;
}

PS: O "codebox" foi gerado com o Pygments.Veja esse post: http://it-ride.blogspot.com/2009/03/syntax-highlighting-on-blogger-with.html

 

Fonte

 

 

 

----

 

Muito boa contribuição Isis. http://forum.imasters.com.br/public/style_emoticons/default/thumbsup.gif

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.