Logo Hardware.com.br
FabianoTraple
FabianoTrapl... Veterano Registrado
1.2K Mensagens 17 Curtidas

Desenvolvendo um Banco de Dados em C - ANSI

#1 Por FabianoTrapl... 30/08/2006 - 11:24
Bem vindo a todos !

Vejo que muitos tem dificuldade em trabalhar e entender o funcionamento de um banco de dados, então como comecei hj a desenvolver um banco de dados meu próprio para usar tanto no LINUX, WINDOWS e UNIX estou disponibilizando os códigos e todo o processo para fazer um BD !

Este tópico é aberto a debate, críticas, dúvidas e sugestões de todos os tipo, não se acanhe em perguntar caso vc seja leigo, terei o maior prazer em explicar detalhadamente todo o processo.

De início criarei um arquivo de cabeçalho chamado "bd.h", o qual usarei como base, e depois irei desenvolver e explicar todas as funções que serão feitas.


/*
Programamador: Fabiano Traple
Nome do arquivo: bd.h
*/

// Difine's de pré-compilação

#define BD_MAX_DB_ABERTOS 256
#define BD_MAX_CAMPOS 128

#define BD_VERDADEIRO 0
#define BD_FALSO -1

#define BD_SN 1 // Equivalente a char
#define BD_TEXTO 2 // Equivalente a uma cadeia char
#define BD_DATA 3 // Equivalente a unsigned long int
#define BD_HORA 4 // Equivalente a unsigned long int
#define BD_NU_INTEIRO 5 // Equivalente a long int
#define BD_NU_DECIMAL 6 // Equivalente a float

// Estrutura do cabeçalho do BD

struct EST_Cabecalho
{
unsigned int TamCabecalho;
char Versao[8];
unsigned long int QtdReg;
unsigned long int QtdRegExcluidos;
unsigned int TamRegistro;
unsigned int QtdCampos;
};

// Estrutura do campo do BD

struct EST_Campo
{
char Descricao[30];
char Tipo;
unsigned int Tamanho;
unsigned int PosIni;
};

// Estrutura que contém os dados de todos arquivos abertos

struct EST_DBAbertos
{
char Apelido[30];
FILE*PonteiroArquivoBD;
unsigned long int PosPonReg;
struct EST_Cabecalho Cabecalho;
struct EST_Campo Campo[BD_MAX_CAMPOS];
};

// Protótipos das funções

char*BD_Erro();

char BD_Criar( char*PA_NomeBD,
struct EST_Campo*PA_Registros,
unsigned int PA_QtdCampos );

char BD_Abrir( char*PA_NomeBD,
char*PA_ApelidoBD );

char BD_Fechar();

char BD_SelecionaBD( char*PA_ApelidoBD );
int BD_NumeroCampo( char*PA_NomeCampo );

char BD_NovoReg();

char BD_GravaSN( char*PA_NomeCampo, char PA_Valor );
char BD_GravaTexto( char*PA_NomeCampo, char*PA_Valor );
char BD_GravaData( char*PA_NomeCampo, char*PA_Valor );
char BD_GravaHora( char*PA_NomeCampo, char*PA_Valor );
char BD_GravaNuInteiro( char*PA_NomeCampo, long int*PA_Valor );
char BD_GravaNuDecimal( char*PA_NomeCampo, float*PA_Valor );

char BD_LerSN( char*PA_NomeCampo, char PA_Valor );
char BD_LerTexto( char*PA_NomeCampo, char*PA_Valor );
char BD_LerData( char*PA_NomeCampo, char*PA_Valor );
char BD_LerHora( char*PA_NomeCampo, char*PA_Valor );
char BD_LerNuInteiro( char*PA_NomeCampo, long int*PA_Valor );
char BD_LerNuDecimal( char*PA_NomeCampo, float*PA_Valor );

unsigned long int BD_RegAtual();
unsigned long int BD_TotalReg();

char BD_IrParaReg( unsigned long int PA_NumeroRegistro );
char BD_IrParaProximoReg();
char BD_IrParaRegAnterior();
char BD_IrParaInicio();
char BD_IrParaFim();

char BD_Inicio();
char BD_Fim();

char BD_MarcarRegParaExclusao();
char BD_ExcluirRegMarcados();

char BD_CriarIndice( char*PA_NomeIN,
char*PA_NomeBD,
char*PA_NomeCampo );

char BD_AbrirIndice( char*PA_NomeIN );

char BD_ReorganizaIndice( char*PA_NomeIN );


Espero a ajuda de todos ao projeto, e desde já agradeço!
Responder
FabianoTraple
FabianoTrapl... Veterano Registrado
1.2K Mensagens 17 Curtidas
#4 Por FabianoTrapl...
30/08/2006 - 15:37
A todos...

Acabei de criar a primeira função que cria o BD e seu cabeçalho, esse código a seguir pertence ao arquivo "bd.c" ...


#include<string.h>
#include<stdlib.h>
#include<stdio.h>

#include "bd.h"

// definindo variáveis globais


// Variável qie irá conter a descrição do último erro

char PR_DesUltErro[256];

//Cria uma lista usando a estrutura EST_DBAbertos para manter uma relação dos arquivos abertos

struct EST_DBAbertos ListaDBAbertos[BD_MAX_DB_ABERTOS];
unsigned int PR_QtdBDAberto = 0; // Quantidade de BD abertos
unsigned int PR_BDAtual = 0; // BD Atualmente ativo

/*
Objetivo da função: Criar um BD vazio, gravando apenas seu cabeçalho
Parâmetro: char*PA_NomeBD > Nome do BD
Parâmetro: struct EST_Campo*PA_Registros > Estrutura do registro que irá conter a lista de campos
Parâmetro: unsigned int PA_QuantCampos > Quantidade de campos da Estrutura do registro
Retorno: Retorna BD_VERDADEIRO caso bem sucedido ou BD_FALSO caso ocorra erro
*/

char BD_Criar( char*PA_NomeBD,
struct EST_Campo*PA_Campos,
unsigned int PA_QtdCampos )
{
//-----> Cria o arquivo de BD

FILE*LO_PonteiroArquivoBD = fopen( PA_NomeBD, "wb&quot;

// Verifica se houve erro ao criar o arquivo

if( LO_PonteiroArquivoBD == NULL )
{
memset( PR_DesUltErro, '\0', 256 );
strcpy( PR_DesUltErro, "Erro ao criar o arquivo [" );
strcat( PR_DesUltErro, PA_NomeBD );
strcat( PR_DesUltErro, "]!\n&quot;
printf("%s\n", PR_DesUltErro);
return( BD_FALSO );
}

//-----> Cria cabeçalho do BD

struct EST_Cabecalho Temp;

Temp.TamCabecalho = sizeof(struct EST_Cabecalho) + PA_QtdCampos*sizeof(struct EST_Campo);
strcpy( Temp.Versao, "20060830&quot; //Data da última alteração feita no código fonte do DB
Temp.QtdReg = 0;
Temp.QtdRegExcluidos = 0;

Temp.TamRegistro = 1; // Adiciona caracter de controle ao tamanho do registro
int LO_Con;
for( LO_Con = 0; LO_Con < PA_QtdCampos; LO_Con++)
{
PA_Campos[LO_Con].PosIni = Temp.TamRegistro;
Temp.TamRegistro = Temp.TamRegistro + PA_Campos[LO_Con].Tamanho;
}

Temp.QtdCampos = PA_QtdCampos;

// Grava cabeçalho

fwrite( &Temp, sizeof(struct EST_Cabecalho), 1, LO_PonteiroArquivoBD);
fwrite( &PA_Campos[0], (PA_QtdCampos*sizeof(struct EST_Campo)), 1, LO_PonteiroArquivoBD);

// fecha o arquivo

fclose( LO_PonteiroArquivoBD );

return( BD_VERDADEIRO );
}


que dureza né !

:]
[email]umbrsuporte@hotmail.com[/email] bebi_demais.gif
FabianoTraple
FabianoTrapl... Veterano Registrado
1.2K Mensagens 17 Curtidas
#5 Por FabianoTrapl...
31/08/2006 - 11:02
A todos...

Estou postando abaixo o código da função AbrirBD() que faz parte do arquivo "bd.c"


/*
Objetivo da função: Abrir um BD
Parâmetro: char*PA_NomeBD > Nome do BD
Parâmetro: char*PA_ApelidoBD > Apelidado do BD
Retorno: Retorna BD_VERDADEIRO caso bem sucedido ou BD_FALSO caso ocorra erro
*/

char BD_Abrir( char*PA_NomeBD,
char*PA_ApelidoBD )
{
//-----> Cria o arquivo de BD

/*
A função open() ao utilizar as flags "r+b"
Caso o arquivo não exista retorna erro
Caso exista abre
*/

ListaDBAbertos[PR_QtdBDAberto].PonteiroArquivoBD = fopen( PA_NomeBD, "r+b&quot;

// Verifica se houve erro ao criar o arquivo

if( ListaDBAbertos[PR_QtdBDAberto].PonteiroArquivoBD == NULL )
{
memset( PR_DesUltErro, '\0', 256 );
strcpy( PR_DesUltErro, "Erro ao abrir o arquivo [" );
strcat( PR_DesUltErro, PA_NomeBD );
strcat( PR_DesUltErro, "]!\n&quot;
printf("%s\n", PR_DesUltErro);
return( BD_FALSO );
}

// Carregando cabeçalho do BD

fread( &ListaDBAbertos[PR_QtdBDAberto].Cabecalho,
sizeof(struct EST_Cabecalho), 1,
ListaDBAbertos[PR_QtdBDAberto].PonteiroArquivoBD);

fread( &ListaDBAbertos[PR_QtdBDAberto].Campo,
(ListaDBAbertos[PR_QtdBDAberto].Cabecalho.QtdCampos*sizeof(struct EST_Campo)), 1,
ListaDBAbertos[PR_QtdBDAberto].PonteiroArquivoBD);

// Adiciona apelido

strcpy( ListaDBAbertos[PR_QtdBDAberto].Apelido, PA_ApelidoBD);


PR_QtdBDAberto++; // Adiciona +1 na quantidade de BD abertos
PR_BDAtual = PR_QtdBDAberto-1; // Altera o BD aberto atual

return( BD_VERDADEIRO );
}



Bom, até aqui td certinho..
[email]umbrsuporte@hotmail.com[/email] bebi_demais.gif
FabianoTraple
FabianoTrapl... Veterano Registrado
1.2K Mensagens 17 Curtidas
#6 Por FabianoTrapl...
31/08/2006 - 11:45
A todos...

Acabei de cria a função que fecha o BD:


/*
Objetivo da função: Fechar um BD aberto anteriormente
Parâmetro: char*PA_ApelidoBD > Apelidado do BD
Retorno: Retorna BD_VERDADEIRO caso bem sucedido ou BD_FALSO caso ocorra erro
*/
char BD_Fechar()
{
// Verifica se existe arquivo aberto

if( PR_QtdBDAberto == 0 )
{
memset( PR_DesUltErro, '\0', 256 );
strcpy( PR_DesUltErro, "BD_Fechar: Nenhum arquivo aberto para ser fechado!" );
printf("%s\n", PR_DesUltErro);
return( BD_FALSO );
}

// Fechando arquivo

int LO_Erro = fclose( ListaDBAbertos[PR_BDAtual].PonteiroArquivoBD );

// Verifica se houve erro ao fechar o arquivo

if( LO_Erro != 0 )
{
memset( PR_DesUltErro, '\0', 256 );
strcpy( PR_DesUltErro, "BD_Fechar: Erro ao fechar o arquivo!" );
printf("%s\n", PR_DesUltErro);
return( BD_FALSO );
}

return( BD_VERDADEIRO );
}


Creio que esteja tudo certinho, com essas 3 funções já podemos criar, abrir e fechar um BD, agora vem a segunda parte, criar um novo registro, gravar dados nele e ler os dados novamente !

Agora a coisa começa a ficar interessante !

Eu aposto que eu bato a velocidade de qualquer BD existente ! hehehehhe quer apostar ?


:mrgreen:
[email]umbrsuporte@hotmail.com[/email] bebi_demais.gif
tetim
tetim Membro Senior Registrado
366 Mensagens 1 Curtida
#9 Por tetim
31/08/2006 - 14:43
FabianoTraple
tetim...

kkkkkk boa, me diz qual é a segurança que os outros BD possuiem ???

Se vc quizer posso adicionar duas ...


Não estou criticando a sua iniciativa, mas isso confunde quem vai começar, pois quem já mexeu sabe que robustez do banco e consistência dos dados está inversamente proporcional à velocidade.

Não vi todo o seu código, mas tem um erro muito grave, que pode ocasionar um buffer overflow.
"Conseguirão parar uma, duas ou até tres flores, mas nunca conseguirão segurar a força de uma primavera..."[Chê Guevara]
FabianoTraple
FabianoTrapl... Veterano Registrado
1.2K Mensagens 17 Curtidas
#10 Por FabianoTrapl...
31/08/2006 - 16:27
tetim...

Eu sei que vc não criticou, nem levei para esse lado :]

Realmente o código que publiquei já está alterado, estou testestando as funções, e conforme vou fazendo isso, vou alterando o que já postei e informando as alterações e o pq...

Falta ainda muios processos de verificação de estouro no uso dos vetores, variáveis e parametros, mas esse é o objetivo, ir eliminando os erros e bugs !

:]
[email]umbrsuporte@hotmail.com[/email] bebi_demais.gif
FabianoTraple
FabianoTrapl... Veterano Registrado
1.2K Mensagens 17 Curtidas
#11 Por FabianoTrapl...
01/09/2006 - 10:18
A todos...

Hoje eu editei todas as funções postadas anteriores, para as que estou usando no momento.

E abaixo irei colocar novas funções que fiz.


/*
Objetivo da função: Selecionar o BD ativo atualmente
Parâmetro: char*PA_ApelidoBD > Apelidado do BD
Retorno: Retorna a posição do apelido na lista de arquivos abertos ou -1 caso não exita
*/
char BD_SelecionaBD( char*PA_ApelidoBD )
{

// Verifica se o apelido dado como paramentro existe na lista de arquivos abertos

int LO_PosLisBD;
for( LO_PosLisBD = 0; LO_PosLisBD < PR_QtdBDAberto; LO_PosLisBD++)
{
if( strcmp( ListaDBAbertos[LO_PosLisBD].Apelido, PA_ApelidoBD ) == 0 )
{
PR_BDAtual = LO_PosLisBD;
return( BD_VERDADEIRO );
}
}

if( LO_PosLisBD == PR_QtdBDAberto )
{
memset( PR_DesUltErro, '\0', 256 );
strcpy( PR_DesUltErro, "BD_SelecionaBD: Apelido desconhecido!" );
printf("%s\n", PR_DesUltErro);
return( BD_FALSO );
}
}



/*
Objetivo da função: Cria um novo registro em branco no fim do BD
Parâmetro: char*PA_ApelidoBD > Apelidado do BD
Retorno: Retorna BD_VERDADEIRO caso bem sucedido ou BD_FALSO caso não
*/
char BD_NovoReg()
{
// Verifica se existe arquivo aberto

if( PR_QtdBDAberto == 0 )
{
memset( PR_DesUltErro, '\0', 256 );
strcpy( PR_DesUltErro, "BD_NovoReg: Nenhum arquivo aberto para ser fechado!" );
printf("%s\n", PR_DesUltErro);
return( BD_FALSO );
}

// Criando registro em branco no final do arquivo

char*LO_RegistroVazio = (char*) malloc( ListaDBAbertos[PR_BDAtual].Cabecalho.TamRegistro );
if( LO_RegistroVazio == NULL )
{
memset( PR_DesUltErro, '\0', 256 );
strcpy( PR_DesUltErro, "BD_NovoReg: Não foi possivel alocar a memória!" );
printf("%s\n", PR_DesUltErro);
return( BD_FALSO );
}

fseek( ListaDBAbertos[PR_BDAtual].PonteiroArquivoBD, 0, SEEK_END);

fwrite( LO_RegistroVazio,
ListaDBAbertos[PR_BDAtual].Cabecalho.TamRegistro, 1,
ListaDBAbertos[PR_BDAtual].PonteiroArquivoBD);

free( LO_RegistroVazio );

ListaDBAbertos[PR_BDAtual].Cabecalho.QtdReg++;

return( BD_VERDADEIRO );
}




/*
Objetivo da função: Grava dado no campo do registro atual
Parâmetro: char*PA_NomeCampo > Nome do campo
Parâmetro: char*PA_Valor > Valor que será gravado
Retorno: Retorna BD_VERDADEIRO caso bem sucedido ou BD_FALSO caso não
*/
char BD_GravaTexto( char*PA_NomeCampo, char*PA_Valor )
{
// Verifica se existe arquivo aberto

if( PR_QtdBDAberto == 0 )
{
memset( PR_DesUltErro, '\0', 256 );
strcpy( PR_DesUltErro, "BD_NovoReg: Nenhum arquivo aberto para ser fechado!" );
printf("%s\n", PR_DesUltErro);
return( BD_FALSO );
}

int LO_NumCampo = BD_NumeroCampo( PA_NomeCampo );
if( LO_NumCampo == BD_FALSO )
{
memset( PR_DesUltErro, '\0', 256 );
strcpy( PR_DesUltErro, "BD_Gravar: Nome do campo desconhecido!" );
printf("%s\n", PR_DesUltErro);
return( BD_FALSO );
}


// Posiciona ponteiro do DB

unsigned long int LO_PosPon = ListaDBAbertos[PR_BDAtual].Cabecalho.TamCabecalho+
((ListaDBAbertos[PR_BDAtual].Cabecalho.QtdReg-1)*
ListaDBAbertos[PR_BDAtual].Cabecalho.TamRegistro)+
ListaDBAbertos[PR_BDAtual].Campo[LO_NumCampo].PosIni;

int LO_TamCampo = 0;
if( ListaDBAbertos[PR_BDAtual].Campo[LO_NumCampo].Tipo != BD_TEXTO )
{
memset( PR_DesUltErro, '\0', 256 );
strcpy( PR_DesUltErro, "BD_GravaTexto: O tipo deste campo não é texto!" );
printf("%s\n", PR_DesUltErro);
return( BD_FALSO );
}
LO_TamCampo = strlen(PA_Valor);
if( LO_TamCampo > ListaDBAbertos[PR_BDAtual].Cabecalho.TamRegistro )
{
LO_TamCampo = ListaDBAbertos[PR_BDAtual].Cabecalho.TamRegistro;
}

fseek( ListaDBAbertos[PR_BDAtual].PonteiroArquivoBD, LO_PosPon, SEEK_SET );
fwrite( PA_Valor, LO_TamCampo, 1, ListaDBAbertos[PR_BDAtual].PonteiroArquivoBD);

return( BD_VERDADEIRO );
}



/*
Objetivo da função: Grava dado no campo do registro atual
Parâmetro: char*PA_NomeCampo > Nome do campo
Parâmetro: long int*PA_Valor > Valor que será gravado
Retorno: Retorna BD_VERDADEIRO caso bem sucedido ou BD_FALSO caso não
*/
char BD_GravaNuInteiro( char*PA_NomeCampo, long int*PA_Valor )
{
// Verifica se existe arquivo aberto

if( PR_QtdBDAberto == 0 )
{
memset( PR_DesUltErro, '\0', 256 );
strcpy( PR_DesUltErro, "BD_NovoReg: Nenhum arquivo aberto para ser fechado!" );
printf("%s\n", PR_DesUltErro);
return( BD_FALSO );
}

int LO_NumCampo = BD_NumeroCampo( PA_NomeCampo );
if( LO_NumCampo == BD_FALSO )
{
memset( PR_DesUltErro, '\0', 256 );
strcpy( PR_DesUltErro, "BD_Gravar: Nome do campo desconhecido!" );
printf("%s\n", PR_DesUltErro);
return( BD_FALSO );
}


// Posiciona ponteiro do DB

unsigned long int LO_PosPon = ListaDBAbertos[PR_BDAtual].Cabecalho.TamCabecalho+
((ListaDBAbertos[PR_BDAtual].Cabecalho.QtdReg-1)*
ListaDBAbertos[PR_BDAtual].Cabecalho.TamRegistro)+
ListaDBAbertos[PR_BDAtual].Campo[LO_NumCampo].PosIni;

int LO_TamCampo = sizeof(long int);
if( ListaDBAbertos[PR_BDAtual].Campo[LO_NumCampo].Tipo != BD_NU_INTEIRO )
{
memset( PR_DesUltErro, '\0', 256 );
strcpy( PR_DesUltErro, "BD_GravaTexto: O tipo deste campo não é um inteiro numérico!" );
printf("%s\n", PR_DesUltErro);
return( BD_FALSO );
}

fseek( ListaDBAbertos[PR_BDAtual].PonteiroArquivoBD, LO_PosPon, SEEK_SET );
fwrite( &PA_Valor, LO_TamCampo, 1, ListaDBAbertos[PR_BDAtual].PonteiroArquivoBD);

return( BD_VERDADEIRO );
}



/*
Objetivo da função: Verificar se o nome do campo existe e retorna a sua posição
Parâmetro: char*PA_NomeCampo > Nome do campo
Retorno: Retorna a posição do campo ou DB_FALSO caso dê erro
*/
int BD_NumeroCampo( char*PA_NomeCampo )
{
int LO_NumCampo;
for( LO_NumCampo = 0; LO_NumCampo < ListaDBAbertos[PR_BDAtual].Cabecalho.QtdCampos; LO_NumCampo++)
{
if( strncmp( ListaDBAbertos[PR_BDAtual].Campo[LO_NumCampo].Descricao, PA_NomeCampo, strlen(PA_NomeCampo) ) == 0 )
{
return( LO_NumCampo );
break;
}
}
return( BD_FALSO );
}


Com essas 4 funções adicionais estou podendo fazer testes de velocidade do BD...
[email]umbrsuporte@hotmail.com[/email] bebi_demais.gif
FabianoTraple
FabianoTrapl... Veterano Registrado
1.2K Mensagens 17 Curtidas
#12 Por FabianoTrapl...
01/09/2006 - 10:29
Abaixo está o codigo que estou usando para testes...


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

#include "bd.h"

// Descomente essas linhas abaixo conforme seu sistema operacional

#define PU_SO linux
//#define PU_SO windows

int main()
{
// Limpa a tela

#if( PU_SO == linux )
{
system( "clear" ); // Essa função limpa a tela no linux
}
#else
{
system( "cls" ); // Essa função limpa a tela no windows
}
#endif

// Inicia o teste

printf( "Iniciando teste de criação do DB...\n" );


// Criando estrutura de um cadastro de clientes para teste

struct EST_Campo Clientes[3];

// Campo 1

memset( Clientes[0].Descricao, '\0', 30);
strcpy( Clientes[0].Descricao, "Nome\n" );
Clientes[0].Tipo = BD_TEXTO;
Clientes[0].Tamanho = 60;

// Campo 2

memset( Clientes[1].Descricao, '\0', 30);
strcpy( Clientes[1].Descricao, "Rua\n" );
Clientes[1].Tipo = BD_TEXTO;
Clientes[1].Tamanho = 40;

// Campo 3

memset( Clientes[2].Descricao, '\0', 4);
strcpy( Clientes[2].Descricao, "Numero\n" );
Clientes[2].Tipo = BD_NU_INTEIRO;
Clientes[2].Tamanho = 4;

printf( "Iniciando!\n" );


// Criando banco de dados

BD_Criar( "Clientes.bd", Clientes, 3 );

// Abrindo banco de dados

BD_Abrir( "Clientes.bd", "Clientes" );

// Cria um novo registro

long int LO_Con;
for( LO_Con = 0; LO_Con < 10000; LO_Con++)
{
BD_NovoReg( "Clientes" );
BD_GravaTexto( "Nome" , "João da Silva Pereira Rocanbole" );
BD_GravaTexto( "Rua" , "Rua do pé de Laranja" );
BD_GravaNuInteiro( "Numero", &LO_Con );
}

// Fechando banco de dados

BD_Fechar( "Clientes" );

printf( "Fim!\n" );

return(0);
}


Agora Tenho uma dúvida...

Nas funções de gravação acima eu gravo campo a campo no registro do BD, o que deixa ele lento ao se gravar grandes blocos sequenciais de informação, estava pensando em cria uma função especial para gravar e ler o registro inteiro !

Será que havera muita diferença?
Quais seriam as aplicações dessa rotina?
Será que tenho que fazer uma função de verificação da estrutura do registro para que não cause erro, nessa nova rotina ?

...
[email]umbrsuporte@hotmail.com[/email] bebi_demais.gif
Responder Tópico
© 1999-2024 Hardware.com.br. Todos os direitos reservados.
Imagem do Modal