A Kivik Base Teste 2.0 foi desenvolvida em C++ como parte de um trabalho de conclusão de curso com o intuito de gerar uma base de dados testes para MySQL ou MongoDB para ser utilizado em pesquisas de desempenho do SGBD. Inicialmente a versão 1.0 gerava um arquivo .txt e somente para MySQL, mas por suas limitação de tamanho, como por exemplo não suportar 10.000.000 de inserts criados pela ferramenta, a versão 1.1 passou a gerar um arquivo .sql.

A versão que será utilizada neste artigo, a 2.0, além de gerar para MySQL também gera para MongoDB. Como essa ferramenta é open source, alterando seu código fonte, é possível gerar base de dados para qualquer SGBD existente, tendo em vista que o mais “difícil” já foi feito, que foi criar um padrão para banco de dados relacionais, neste caso o MySQL e, outro padrão para SGBDs não relacionais, como é o caso do MongoDB.

O algoritmo de multiplicação dos inserts está inserido duas vezes dentro do código fonte: uma para MySQL e outra para MongoDB. Então, ao alterar o código fonte, a primeira informação que você tem que ter em mente é se o SGBD é relacional ou não relacional.

Após fazer o download da ferramenta (vide seção Links), observe que o arquivo tem quatro arquivos, sendo um deles o executável e outro o código fonte salvo em formato .cpp. Para rodar este arquivo será necessário um compilador da linguagem C, como por exemplo, o DEV C++.

Por apenas ser executada, e como a Kivik trabalha com manipulação da memória do seu computador para gerar as tabelas ou documentos e criar o arquivo com a base de dados a ser utilizada, lembre-se de desabilitar o seu firewall antes de executá-la.

Iniciaremos esse artigo separando o algoritmo especifico do MongoDB, e a partir dele, mostraremos como utilizá-lo e alterá-lo. Na Listagem 1 temos o algoritmo de criação de documentos, geração e multiplicação de inserts.


printf ("Nome do documento: "); 
// versões futuras numero de documentos será aleatório
scanf ("%s", &documento);

listaDeColunas = NULL;
printf ("\nDocumentos prontas para serem inseridas");

printf ("\n\nDigite o número de chaves do documento: "); 
// versões futuras numero de colunas será aleatório
scanf ("%d", &qtdColunas);

for (cont=0; cont<qtdColunas; cont++){

printf ("Digite o nome da chave: "); 
// versões futuras nome da coluna será gerado randomicamente
scanf ("%s", &addcol);

novo = (col*)malloc(sizeof(col));
memcpy(novo->nome, addcol, 50);
novo->tipo = getchar();

novo -> prox = NULL;
if(listaDeColunas == NULL){
         listaDeColunas = novo;
} else {
         aux = listaDeColunas;
         while (aux -> prox != NULL) aux = aux->prox;
         aux -> prox = novo;
} 

}

  cls();

exibirCabecalho();

if (fp) {

fprintf (fp, "\n\n\nuse %s;", documento);

aux = listaDeColunas;

printf ( "insira quantidade de dados a serem criados: ");
scanf ("%d", &quant_dados);


for (i=quant_dados; i>=1; i--)
{

fprintf (fp, "\ndb.%s.insert( {", documento);

aux = listaDeColunas;

  while (aux != NULL){
     if(aux -> prox == NULL){
        fprintf (fp, "%s:\"%dkivik\"});", aux -> nome,  rand() % 200000);
        break;
    }else{
        if (aux == listaDeColunas){
            fprintf (fp, "%s:\"%d\", ", aux -> nome,  i);
        }else{
        fprintf (fp, "%s:\"kivik%d\", ", aux -> nome,  rand() % 200000);      
    }
    aux = aux -> prox;
  }
}
}
fclose(fp);
}
getch();
Listagem 1. Algoritmo para bancos não relacionais

Nas linhas um e dois o código apenas inserimos um texto na tela e pedimos a inserção de uma variável do tipo string. Nas linhas quatro e cinco é separado um endereço de memória para poder começar a inserir os dados e sua mensagem de confirmação, respectivamente. Nas linhas sete e oito é onde o usuário insere a quantidade de chaves/tabelas que a ferramenta deve ter: repare no comentário no final da linha sete que mostra que podemos usar o mesmo código para gerar bases testes para todos os SGBDs existentes.

Na linha 10 é aberto um laço de repetição com base no valor anteriormente digitado pelo usuário. Nas linhas 12 e 13 temos uma mensagem ao usuário pedindo para inserir o nome da chave/tabela. Em versões futuras, a ferramenta poderá gerar esses nomes aleatoriamente caso o usuário permita. Nas linhas 15 a 17 é alocado um espaço na memória e todo dado inserido é copiado para um novo endereço de memória, dado esse que é copiado de forma binária, já que esses dados estão sendo tratados como vetores.

Na linha 19 criamos um novo espaço de memória vazio para continuar com a repetição. Nas linhas 20 a 28 do código existe uma condição de apenas duas possibilidades: verificar se a lista onde foram inseridos os dados está vazia ou já contém algum dado. Lembre-se que é nessas linhas onde ocorre a inserção dos dados na memória.

Nas linhas 30 e 32 são chamadas duas funções que estão fora desse algoritmo base (algoritmo base da ferramenta).

A linha 34 do algoritmo base é onde ocorre a abertura do arquivo onde serão inseridos os dados gerados. Esse arquivo é criado fora do algoritmo base nessa versão, com o intuito de sempre ser o mesmo arquivo, não importando qual SGBD o usuário escolha para gerar a base de dados a ser utilizada em seus testes.

Na linha 36 temos a sintaxe de criação de documentos do MongoDB: lembrando que estamos utilizando a versão do algoritmo base para MongoDB neste artigo. Já nas linhas 40 e 41 é onde ocorre a inserção da quantidade de dados a serem criados.

Das linhas 44 até 67 é onde ocorre o laço que gera os inserts de acordo com o número de dados digitado pelo usuário. Caso o usuário queira criar base de dados testes para outros SGBDs além do MongoDB e MySQL (gerados por esta versão), o usuário deverá alterar apenas a sintaxe das linguagens contidas nas linhas 36, 47, 53, 57 e 59 do algoritmo.

Lembre-se que a sintaxe entre bancos de dados relacionais e não relacionais difere bastante, então, se o usuário for gerar bases de dados para bancos de dados relacionais, orienta-se a utilização do algoritmo base que está gerando para MySQL.

Já fora do algoritmo base apresentado, entre as linhas 200 e 217 do código fonte, o usuário percebe uma função utilizada para apagar o texto da tela da ferramenta. Esta função foi utilizada porque utilizar a função localizada na biblioteca stdlib.h não funcionava corretamente. Na Listagem 2 temos o referido código.


void cls() 
{ 
 HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE); 
 
 CONSOLE_SCREEN_BUFFER_INFO info; 
 GetConsoleScreenBufferInfo( hCon, &info ); 
 
 COORD home = { 0, 0 }; 
 DWORD nchars = (DWORD)info.dwSize.X * info.dwSize.Y; 
 DWORD nwritten; 
 
 // Fill the entire screen with blanks. 
 
 FillConsoleOutputCharacter( hCon, ' ', nchars, home, &nwritten ); 
 FillConsoleOutputAttribute( hCon, info.wAttributes, nchars, home, &nwritten); 
 
 SetConsoleCursorPosition( hCon, home ); 
}
Listagem 2. Função da biblioteca stdlib.h

Como sabemos, a sintaxe dos inserts entre todos os SGBDs se difere, principalmente entre bancos de dados relacionais e não relacionais. Em bancos de dados não relacionais, como é o caso do MongoDB, sua sintaxe funciona no método chave/valor, onde a cada chave digitada o valor deve ser inserido em seguida. Já nos bancos de dados relacionais, suas colunas são inseridas primeiro e, depois os valores em sua uma ordem respectiva a das colunas. Essa pequena diferença de sintaxe entre bancos relacionais e não relacionais foi crucial para o algoritmo base.

Enquanto que para MongoDB o usuário insere o número de chaves que bem entender na versão 2.0, no caso do MySQL o número de colunas é fixo em 10, podendo ser alterado nas linhas 63 e 64 de seu algoritmo base. Na Listagem 3 temos o algoritmo base na versão dos bancos de dados relacionais.


printf ("Nome da tabela: "); // versões futuras numero de tabelas será aleatório
scanf ("%s", &tabela);
            
 listaDeColunas = NULL;
 printf ("\nColunas prontas para serem inseridas");
 
 printf ("\n\nDigite 10 para gerar 10 colunas: "); 
 // versões futuras numero de colunas será aleatório
 scanf ("%d", &qtdColunas);
          
 printf ("\nPrimeira coluna eh primary key.\n\n"); 
            
 for (cont=0; cont<qtdColunas; cont++){
 
 printf ("Digite o nome da coluna: "); 
 // versões futuras nome da coluna será gerado randomicamente
  scanf ("%s", &addcol);
  printf ("insira alguma variavel do tipo char: "); 
  // versões futuras poderá escolher tipo da variavel nesse momento
  scanf ("%s", &tipo);
 
  novo = (col*)malloc(sizeof(col));
  memcpy(novo->nome, addcol, 50);
  novo->tipo = getchar();
 
  novo -> prox = NULL;
  if(listaDeColunas == NULL){
      listaDeColunas = novo;
  } else {
     aux = listaDeColunas;
     while (aux -> prox != NULL) aux = aux->prox;
     aux -> prox = novo;
 } 
            
}
            
cls();
            
exibirCabecalho();
            
if (fp) {
            
  fprintf (fp, "\n\n\ncreate table %s (", tabela);
            
  aux = listaDeColunas;
  while (aux != NULL){
     fprintf (fp, "\n %s varchar(50),", aux -> nome); 
     // versões futuras criar cases para tipo de variavel (recomendavel aqui)
    aux = aux -> prox;
   }
            
   fprintf(fp, "\nPRIMARY KEY (%s)\n);\n\n", listaDeColunas->nome);
            
   printf ( "insira quantidade de dados a serem criados: ");
   scanf ("%d", &quant_dados);
   
   for (i=quant_dados; i>=1; i--)
   {
            
     fprintf (fp, "\ninsert into %s (", tabela);
            
     aux = listaDeColunas;
 
     while (aux != NULL){
        if(aux -> prox == NULL){
           fprintf (fp, "%s) VALUES ( %d, 'kivik%d', 'kivik%d', '%dkivik', 
           'kivik%d', '%dkivik', 'kivik%d', '%dkivik', 'kivik%d', '%dkivik');"
           , aux -> nome, i, rand() % 200000, rand() % 200000, rand() % 200000, 
           rand() % 200000, rand() % 200000, rand() % 200000, rand() % 200000, rand() 
           % 200000, rand() % 200000);
       }else{
           fprintf (fp, "%s,", aux -> nome );      
       }
       aux = aux -> prox;
    }
  }
  fclose(fp);
}
getch();
Listagem 3. Código para alterações com banco de dados relacionais

O uso da Kivik Base Teste é bem simples. Inicialmente, sugerimos que o usuário, para não ter possíveis problemas com seu firewall, execute a ferramenta como administrador. Para este exemplo inserimos a ferramenta na raiz de uma partição qualquer, mas isso não é obrigatório. Lembre-se que o arquivo com os dados será gerado no mesmo local onde a ferramenta estiver.

Na tela inicial da ferramenta, as três primeiras linhas são o cabeçalho da ferramenta. A função deste cabeçalho se encontra entre as linhas 7 e 13 do seu código fonte. Na linha 13 chamamos aquela função que apaga o que está escrito na tela da ferramenta.

As três últimas linhas da tela inicial da ferramenta é um pequeno label, onde podemos escolher uma das três opções apresentadas, como mostra a Figura 1. Após a escolha a ferramenta fecha.

Tela inicial Kivik Base Teste 2.0
Figura 1. Tela inicial Kivik Base Teste 2.0

Para este artigo vamos mesclar o uso da mesma, gerando uma base de dados apenas, e duas bases de teste.

Após escolher para qual SGBD o usuário quer gerar a base de dados, a tela que aparece na versão 2.0 pergunta qual o nome da tabela/documento e, em seguida, pede para inserir o número de colunas/chaves.

Na versão para gerar uma base de dados para MySQL, a cada nome de coluna inserido pede-se para digitar uma variável do tipo char (uma letra) (não pode ser "ç" ou caracteres especiais). Repare que esse pedaço corresponde as linhas 16 e 17 do algoritmo base para bancos de dados relacionais. Na Figura 2 temos um exemplo de como se encontrará a tela da ferramenta neste momento.

Inserindo dados na Kivik Base Teste 2.0
Figura 2. Inserindo dados na Kivik Base Teste 2.0

Após inserir o nome da última coluna/chave a tela da ferramenta se apaga, isso ocorre porque nas linhas 34 ou 30 (o número de linhas difere um pouco por causa da sintaxe de cada tipo de SGBD) do algoritmo base é chamado a função utilizada para apagar os dados da tela.

O arquivo onde serão inseridos os dados é criado no momento em que a ferramenta é aberta, por causa disso, foi sugerido que utilize a ferramenta em modo administrador, para não perder tempo extra com seu firewall ou antivírus.

Neste momento a tela da ferramenta mostra o cabeçalho e pede ao usuário para inserir a quantidade de dados que deseja. O usuário pode solicitar a criação de nenhum dado ou de mais de 10.000.000 de inserts de uma vez. Enquanto a ferramenta não terminar de gerar os dados o usuário não poderá fechar a janela: ao clicar em “enter”, a ferramenta fecha, então nada ocorre.

Por segurança não utilize o documento de forma nenhuma até terminar de gerar os dados, pois isso pode travar tanto a ferramenta, quanto o local onde ela está e outros softwares que estejam abertos.

Na Figura 3 a seguir um exemplo da tela da ferramenta neste momento.

Segunda tela da Kivik Base Teste 2.0
Figura 3. Segunda tela da Kivik Base Teste 2.0

Ao final a ferramenta gera um arquivo do tipo .doc, mas isso pode ser alterado na linha 38 do código fonte. Foi escolhido este tipo de arquivo por causa do seu fácil manuseio. Repare que nesta linha do código fonte temos a expressão "a+": ela foi escolhida pois cria um documento em branco, e se o documento já existir, ele insere os dados criados no final do documento.

Se o usuário tentar criar uma nova base de dados com um documento com o mesmo nome, os dados que a ferramenta criar serão inseridos a partir do final desse documento. Caso o arquivo esteja aberto quando o usuário criar uma nova base de dados, basta o usuário não salvar possíveis alterações feitas no arquivo, fechar e abrir novamente, que a nova base de dados estará criada ao final do arquivo.

Veja um exemplo desse documento na Figura 4.

Parte do arquivo criado na Kivik Base Teste 2.0
Figura 4. Parte do arquivo criado na Kivik Base Teste 2.0

Veja na figura que temos duas bases criadas: a primeira para MySQL a quantidade de dados foi 0; já a segunda base de dados, que foi gerada para MongoDB, têm 10.000.000 de inserts.

Como podemos ver a ferramenta gera o número da primary key em ordem decrescente, isso acontece para podermos visualizar de forma mais rápida a quantidade de dados gerados. Nesta versão todas as colunas são do tipo varchar, mas o usuário não precisa se preocupar com a primary key tendo apenas números, mas caso queira alterar o tipo da variável da primary key, nesta versão o usuário tem que editar o documento gerado.

O código completo da ferramenta encontra-se na Listagem 4.


#include <stdio.h>
#include <conio.h>
#include <windows.h>
#include <stdlib.h>
#include <time.h> 

void exibirCabecalho(){
  printf ("kivik base teste versao 1.1 - MySQL\n");
  printf ("Desenvolvido por: Alam Maia da Silva Vianna\n");
  printf ("Colaboradores: IRC, servidor RIZON, canal #gold_code\n\n\n");
}

  void cls();

main (void) {
  
  struct col{
  char nome[50];
  char tipo;
  int nucolna;
  col *prox;
}*listaDeColunas;

    char tabela[50];
    int qtdColunas=0;
    int cont;
    int i, quant_dados;
    char addcol[50];
  char *tipo;
  char addtipo;
//  int i, cont; /*retirado na retirada das funções*/
  col *aux, *novo;
          
    srand(time(NULL));

    exibirCabecalho();
    
    printf ("Nome da tabela: "); // versões futuras numero de tabelas será aleatório
    scanf ("%s", &tabela);
          
// cria lista vazia
  listaDeColunas = NULL;
  printf ("\nColunas prontas para serem inseridas");

    printf ("\n\nDigite 10 para gerar 10 colunas: "); 
    // versões futuras numero de colunas será aleatório
    scanf ("%d", &qtdColunas);
    
    printf ("\nPrimeira coluna eh primary key.\n\n"); 
    
    for (cont=0; cont<qtdColunas; cont++){
  //col *aux, *novo; // ignorado na retirada das funções

          printf ("Digite o nome da coluna: "); 
          // versões futuras nome da coluna será gerado randomicamente
          scanf ("%s", &addcol);
          printf ("insira alguma variavel do tipo char: "); 
          // versões futuras poderá escolher tipo da variavel nesse momento
//        fflush(stdio);
          scanf ("%s", &tipo);

          novo = (col*)malloc(sizeof(col));
          memcpy(novo->nome, addcol, 50);
          novo->tipo = getchar();

          novo -> prox = NULL;
          if(listaDeColunas == NULL){
                      listaDeColunas = novo;
          } else {
                      aux = listaDeColunas;
                      while (aux -> prox != NULL) aux = aux->prox;
                      aux -> prox = novo;
          } 
          
          }
          
                      cls();
          
          exibirCabecalho();
          

          FILE *fp = fopen("kivik.sql", "a+");
          
          if (fp) {
          
          fprintf (fp, "\ncreate table %s (", tabela);
          
          //col *aux; 
          aux = listaDeColunas;
          while (aux != NULL){
                      fprintf (fp, "\n %s varchar(50),", aux -> nome); 
                      // versões futuras criar cases para tipo de variavel (recomendavel aqui)
                      aux = aux -> prox;
          }
          
          fprintf(fp, "\nPRIMARY KEY (%s)\n);\n\n", listaDeColunas->nome);
          
          printf ( "insira quantidade de dados a serem criados: ");
          scanf ("%d", &quant_dados);
          
          for (i=quant_dados; i>=1; i--)
          {
          
          fprintf (fp, "\ninsert into %s (", tabela);
          
//          col *aux; 
          aux = listaDeColunas;

            while (aux != NULL){
                 if(aux -> prox == NULL){
                     fprintf (fp, "%s) VALUES ( %d, 'kivik%d', 'kivik%d', '%dkivik', 
                     'kivik%d', '%dkivik', 'kivik%d', '%dkivik', 'kivik%d', '%dkivik');"
                     , aux -> nome, i, rand() % 2000, rand() % 2000, rand() % 2000, 
                     rand() % 2000, rand() % 2000, rand() % 2000, rand() % 2000, rand() % 2000, 
                     rand() % 2000);
                 }else{
                     fprintf (fp, "%s,", aux -> nome );      
                 }
                 aux = aux -> prox;
              }
          }
          fclose(fp);
          }
          
           getch();
          return 0;
}


void cls() 
{ 
HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE); 

CONSOLE_SCREEN_BUFFER_INFO info; 
GetConsoleScreenBufferInfo( hCon, &info ); 

COORD home = { 0, 0 }; 
DWORD nchars = (DWORD)info.dwSize.X * info.dwSize.Y; 
DWORD nwritten; 

// Fill the entire screen with blanks. 

FillConsoleOutputCharacter( hCon, ' ', nchars, home, &nwritten ); 
FillConsoleOutputAttribute( hCon, info.wAttributes, nchars, home, &nwritten); 

SetConsoleCursorPosition( hCon, home ); 
}
Listagem 4. Código completo da ferramenta

A ferramenta está em constante desenvolvimento e, caso queira contribuir, basta alterar o código no github (vide seção Links). Caso altere o código fonte para gerar para outro SGBD pedimos que suba a parte do código que alterou, assim poderemos atualizá-la com mais um SGBD, mas não precisa arrumar o menu inicial, bastando apenas informar o SGBD nos comentários do código.

Nota: Kivik 2.0