Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

Gab RM SQLite

Gab!

Member

Gab RM SQLite
Version: 1.0.0
By: Gab!



Introduction

Allows you to create SQL databases locally, without server or Internet connection, of course, also will not allow others to see what's in this file unless you send it to them.

Features

Screenshots

Imperceptible by images

Demo

Not necessary, I think.

Script

Code:
 

#==============================================================================

# ** SQLiteAPI

#------------------------------------------------------------------------------

#  Importa todas as funções usadas pelo script, a partir da DLL do SQLite3

#==============================================================================

module SQLiteAPI

  FUNCTIONS = {}

  DLL       = "System/SQLite3"

  

  # Prototypes

  [

    [:open,          "PP",   "L"], [:prepare,       "LPIPI", "I"],

    [:sleep,         "L",    "I"], [:step,          "L",     "I"],

    [:column_count,  "L",    "I"], [:column_name,   "LI",    "P"],

    [:column_type,   "LI",   "I"], [:column_text,   "LI",    "P"],

    [:column_int,    "LI",   "I"], [:column_double, "LI",    "D"],

    [:finalize,      "L",    "I"], [:close,         "L",     "I"],

    [:backup_init,   "LPLP", "L"], [:backup_step,   "LI",    "I"], 

    [:backup_finish, "L",    "I"], [:errmsg,        "L",     "P"]

  

  ].each{|func, arg1, arg2|

    FUNCTIONS[func] = Win32API.new(DLL, "sqlite3_#{func.to_s}", arg1, arg2)

    define_method(func){|*args| FUNCTIONS[func].call(*args) }

    module_function(func)

  }

  

  # Constantes

  SQLITE_OK         =   0

  SQLITE_ERROR      =   1

  SQLITE_INTERNAL   =   2

  SQLITE_PERM       =   3

  SQLITE_ABORT      =   4

  SQLITE_BUSY       =   5

  SQLITE_LOCKED     =   6

  SQLITE_NOMEM      =   7

  SQLITE_READONLY   =   8

  SQLITE_INTERRUPT  =   9

  SQLITE_IOERR      =  10

  SQLITE_CORRUPT    =  11

  SQLITE_NOTFOUND   =  12

  SQLITE_FULL       =  13

  SQLITE_CANTOPEN   =  14

  SQLITE_PROTOCOL   =  15

  SQLITE_EMPTY      =  16

  SQLITE_SCHEMA     =  17

  SQLITE_TOOBIG     =  18

  SQLITE_CONSTRAINT =  19

  SQLITE_MISMATCH   =  20

  SQLITE_MISUSE     =  21

  SQLITE_NOLFS      =  22

  SQLITE_AUTH       =  23

  SQLITE_FORMAT     =  24

  SQLITE_RANGE      =  25

  SQLITE_NOTADB     =  26

  SQLITE_NOTYPE     =  27

  SQLITE_ROW        = 100

  SQLITE_DONE       = 101

  

  SQLITE_INTEGER    = 1

  SQLITE_FLOAT      = 2

  SQLITE_TEXT       = 3

  SQLITE_BLOB       = 4

  SQLITE_NULL       = 5  

  

  # Função auxiliar

  module_function

  def errorStr(id)

    case id

    when SQLITE_OK;         "Sem erros"

    when SQLITE_ERROR;      "Erro na consulta ou database não encontrada"

    when SQLITE_INTERNAL;   "Erro interno no SQLite"

    when SQLITE_PERM;       "Permissão de acesso negada"

    when SQLITE_ABORT;      "Rotina de callback abortou"

    when SQLITE_BUSY;       "Arquivo de database trancado"

    when SQLITE_LOCKED;     "Uma tabela na database está trancada"

    when SQLITE_NOMEM;      "Falha ao alocar memória"

    when SQLITE_READONLY;   "Tentativa de escrever na database somente-leitura"

    when SQLITE_INTERRUPT;  "Operação terminada pelo sqlite3_interrupt()"

    when SQLITE_IOERR;      "Algum tipo de erro de I/O ocorreu"

    when SQLITE_CORRUPT;    "A imagem da database está corrompida"

    when SQLITE_NOTFOUNT;   "Código desconhecido para sqlite3_file_control()"

    when SQLITE_FULL;       "Inserção falhou por que a database está cheia"

    when SQLITE_CANTOPEN;   "Não foi possível abrir o arquivo da database"

    when SQLITE_PROTOCOL;   "Protocolo para trancar a database falhou"

    when SQLITE_EMPTY;      "A database está vazia"

    when SQLITE_SCHEMA;     "A database mudou seu esquema"

    when SQLITE_TOOBIG;     "A String ou BLOB excedeu o limite de tamanho"

    when SQLITE_CONSTRAINT; "Abortar devido à violação de restrição"

    when SQLITE_MISMATCH;   "Tipos de dados incompatíveis"

    when SQLITE_MISUSE;     "Biblioteca usada incorretamente"

    when SQLITE_NOLFS;      "Uso de recursos do sistema operacional não suportado no host"

    when SQLITE_AUTH;       "Autorização negada"

    when SQLITE_FORMAT;     "Erro de formato da database auxiliar"

    when SQLITE_RANGE;      "2º parâmetro para o sqlite3_bind() fora da faixa"

    when SQLITE_NOTABD;     "Arquivo aberto não é uma database"

    when SQLITE_NOTYPE;     "Tipo de dado desconhecido"

    when SQLITE_ROW;        "sqlite3_step() tem outra linha pronta"

    when SQLITE_DONE;       "sqlite3_step() terminou a execução"

    end

  end

  

  # Classe de erros

  class Error < Exception

  end

end

 

 

#==============================================================================

# ** SQLite

#------------------------------------------------------------------------------

#  Classe que gerencia as conexões com databases de modo orientado.

#==============================================================================

class SQLite

  #-------------------------------------------------------------------------

  # * Atributos

  #-------------------------------------------------------------------------

  attr_reader :closed

  attr_reader :filename

  

  #-------------------------------------------------------------------------

  # * Inicialização

  #   + filename : Nome de criação da database

  #-------------------------------------------------------------------------

  def initialize(filename = "#{__FILE__}.db")

    # Variáveis de instância

    @filename     = filename

    @closed       = false

    @queryRunning = false

    

    # Abre a conexão

    db = [0].pack("L*")

    SQLiteAPI.open(filename, db);

    @handle = db.unpack("L*").first

    

    # Define o finalizador para liberar o acesso à database

    ObjectSpace.define_finalizer(self, method(:finalize))

  end

  

  #-------------------------------------------------------------------------

  # * Faz uma query recursiva para um bloco, retornando para ele cada linha

  #   que foi recebida pela query.

  #   + str    : Query SQL

  #   + &block : Bloco que receberá as linhas (deve receber um argumento)

  #-------------------------------------------------------------------------

  def recursiveQuery(str, &block)

    # Retorna se já estiver rodando uma query

    return if @queryRunning

    

    # Checa se o arquivo está aberto

    check_closed

    @queryRunning = true

 

    # Prepara a query

    buffer  = [0].pack("L*")

    rc      = SQLiteAPI.prepare(@handle, str, -1, buffer, 0)

    query   = buffer.unpack("L*").first

    

    # Se houver problemas, finaliza e imprime o erro (sintaxe, tabela inexistente, etc)

    if (rc != SQLiteAPI::SQLITE_OK)

      raise(SQLiteAPI::Error, SQLiteAPI.errorStr(rc).force_encoding("ASCII-8BIT") + ": " + SQLiteAPI.errmsg(@handle).to_s, caller)

    end

    

    # Roda um passo da query (geralmente retornando uma linha)

    rc = SQLiteAPI.step(query)

    while (rc != SQLiteAPI::SQLITE_DONE)

      case rc

      when SQLiteAPI::SQLITE_BUSY # Se não estiver completa, espera 0.01s.

        SQLiteAPI.sleep(10)

        

      when SQLiteAPI::SQLITE_ERROR # Se houver erro, imprime o erro e finaliza

        raise(SQLiteAPI::Error, SQLiteAPI.errmsg(@handle).to_s, caller)

        break;

        

      when SQLiteAPI::SQLITE_ROW # Se tiver recebido corretamente a linha, trata

        rowdata = {}

        

        # Contagem de colunas

        n = SQLiteAPI.column_count(query)

        

        # Recebimento da linha

        n.times{|i|

          # Nome e tipo da coluna

          name = SQLiteAPI.column_name(query, i).to_s

          type = SQLiteAPI.column_type(query, i).to_i

          

          case type

          when SQLiteAPI::SQLITE_INTEGER # Inteiro

            rowdata[name] = SQLiteAPI.column_int(query, i).to_i

          when SQLiteAPI::SQLITE_FLOAT   # Float

            rowdata[name] = SQLiteAPI.column_double(query, i).to_f

          when SQLiteAPI::SQLITE_TEXT    # Texto

            rowdata[name] = SQLiteAPI.column_text(query, i).to_s

          when SQLiteAPI::SQLITE_BLOB    # Blob (não implementado)

            warn("Leitura de BLOB não implementada")

            rowdata[name] = ""

          when SQLiteAPI::SQLITE_NULL    # Nulo

            rowdata[name] = nil

          else                           # Desconhecido

            raise(SQLiteAPI::Error, SQLiteAPI.errorStr(SQLiteAPI::NOTYPE), caller)

          end

        }

        

        # Retorna a linha para o bloco

        # Se o retorno for :breakRecursiveQuery, para a execução

        break if block.call(rowdata) == :breakRecursiveQuery

      end

      

      # Adquire informação sobre a próxima informação

      rc = SQLiteAPI.step(query)

    end

    

    # Habilita novamente a rodar queries

    @queryRunning = false

    

    # Finaliza processo atual

    SQLiteAPI.finalize(query)

    

    # Retorna o próprio objeto

    return self

  end

  

  #-------------------------------------------------------------------------

  # * Faz uma query não recursiva e retorna todas as linhas recebidas

  #   + str : Query a ser executada

  #-------------------------------------------------------------------------

  def query(str)

    data = []

    recursiveQuery(str){|rowdata| data << rowdata}

    return data

  end

  

  #-------------------------------------------------------------------------

  # * Retorna todas as tabelas existentes na database

  #-------------------------------------------------------------------------

  def getTables

    # Checa se a database está aberta

    check_closed

    

    # Seleciona todas as tabelas

    tabs = []

    recursiveQuery("SELECT name FROM sqlite_master WHERE type = 'table'"){|data|

      # Armazena seu nome

      tabs << data['name']

    }

    

    # Retorna nomes

    return tabs

  end

  

  #-------------------------------------------------------------------------

  # * Retorna colunas existentes na tabela

  #   + table : Tabela que será consultada

  #-------------------------------------------------------------------------

  def getColumns(table)

    # Checa se a database está aberta

    check_closed

    

    columns = []

    

    # Seleciona valores quaisquer

    buffer  = [0].pack("L*")

    rc      = SQLiteAPI.prepare(@handle, "SELECT * FROM `#{table}`", -1, buffer, 0)

    query = buffer.unpack("L*").first

    

    # Faz a contagem de colunas

    n = SQLiteAPI.column_count(query)

    

    # Adquire nome das colunas

    n.times{|i| columns << SQLiteAPI.column_name(query, i).to_s }

        

    # Finaliza query

    SQLiteAPI.finalize(query)

    

    # Retorna colunas compactadas (sem nil) e únicas.

    return columns.compact.uniq

  end

  

  #-------------------------------------------------------------------------

  # * Realiza backup da database para um arquivo

  #   + to : Arquivo de destino ou outra instância de database

  #-------------------------------------------------------------------------

  def backup(to = ("Backup#{__FILE__}#{Time.new.to_i}.db"))

    # Checa se a database está aberta

    check_closed

    

    if (to.is_a?(String))

      # Abre uma handle para o arquivo de destino

      db = [0].pack("L*")

      SQLiteAPI.sqlite3_open(to, db);

      toHandle = db.unpack("L*").first

    elsif (to.is_a?(SQLite))

      # Copia para outra database aberta

      toHandle = to.instance_eval{@handle}

    else

      # Não suportado

      raise(ArgumentError, "Tipo não suportado: #{to.class}")

    end

    

    # Inicia backup

    pBackup = SQLiteAPI.sqlite3_backup_init(toHandle, "main", @handle, "main")

    

    # Faz o backup, se houver permissão/necessidade

    if (pBackup != SQLiteAPI::SQLITE_OK)

      SQLiteAPI.sqlite3_backup_step(pBackup, -1);

      SQLiteAPI.sqlite3_backup_finish(pBackup);

    end

    

    # Fecha a handle da memória

    SQLiteAPI.sqlite3_close(toHandle)

   

    # Retorna o próprio objeto

    return self

  end

  

  #-------------------------------------------------------------------------

  # * Faz o contrário do backup: Copia de um arquivo para a database atual

  #   + file : Arquivo lido para recuperar a database atual

  #-------------------------------------------------------------------------

  def restore(file)

    # Checa se a database não foi fechada

    check_closed

    

    # Restora

    tmpClose{SQLite.new(file).backup(self).finalize}

    

    # Retorna a própria database

    return self

  end

  

  #-------------------------------------------------------------------------

  # * Fecha temporáriamente a database para permitir acesso ao arquivo

  #   + &block : Bloco que será rodado enquanto a database estiver fechada

  #-------------------------------------------------------------------------

  def tmpClose(&block)

    # Checa se o arquivo já não está fechado

    check_closed

    

    # Fecha a database

    SQLiteAPI.sqlite3_close(@handle)

    

    # Determina que o arquivo já está fechado

    @closed = true

    

    # Chama o bloco

    block.call

    

    # Abre novamente a handle para a database

    @closed = false

    db = [0].pack("L*")

    SQLiteAPI.sqlite3_open(@filename, db);

    @handle = db.unpack("L*").first

    

    # Retorna a própria database

    return self

  end

  

  #-------------------------------------------------------------------------

  # * Finaliza a database, fechando a conexão com o arquivo

  #-------------------------------------------------------------------------

  def finalize

    SQLiteAPI.sqlite3_close(@handle)

    @closed = true

    @handle = 0

  end

  

  

  private

  #-------------------------------------------------------------------------

  # * Checa se o arquivo está fechado. Caso esteja, chama a excessão

  #   SQLiteAPI::Error, para previnir acessos inválidos.

  #-------------------------------------------------------------------------

  def check_closed

    return unless @closed

    raise(SQLiteAPI::Error, "Database fechada!", caller)

  end

end

 

#==============================================================================

# ** MemorySQLiteAPI

#------------------------------------------------------------------------------

#  Classe para iniciar database na memória. Suporta todos os métodos da SQLite

#==============================================================================

class MemorySQLite < SQLite

  #-------------------------------------------------------------------------

  # * Inicialização

  #-------------------------------------------------------------------------

  def initialize

    super(':memory:')

  end

  

  #-------------------------------------------------------------------------

  # * Como é um bloco na memória, não são necessários processamentos adicio-

  #   nais para permitir acesso ao arquivo.

  #-------------------------------------------------------------------------

  def tmpClose(&block)

    # Checa se a conexão está ativa

    check_closed

    

    # Chama o block

    block.call

    

    # Retona a própria database

    return self

  end

  

  #-------------------------------------------------------------------------

  # * Retira a definição do método filename, para que não ocorra uma tenta-

  #   tiva de acesso à um arquivo inexistente.

  #-------------------------------------------------------------------------

  undef filename

end

 
Sorry for Portuguese comments; anyway, I leave all you'll need to develop in "Instructions" section, look at "To Developers" topic.

Instructions

- Put SQLite3.dll (download here) on Project's "System" folder.
- Put script above "Main" script.
- To developers:
Code:
 

# Create new DB file. If already exists, open it.

# To create a memory database (no file), use MemorySQLite instead SQLite with no arguments.

sqlite = SQLite.new("File.db")

 

# Doing queries

sqlite.query("CREATE TABLE `TestTable` (C1 INT(9), C2 TEXT)")

sqlite.query("INSERT INTO `TestTable` VALUES (2012, 'ABCD')")

sqlite.query("INSERT INTO `TestTable` VALUES (2013, 'EFGH')")

 

# Recursive Query

sqlite.recursiveQuery("SELECT * FROM `TestTable`"){|line|

  print line["C1"] # First loop: 2012 // Second loop: 2013

  print line["C2"] # First loop: ABCD // Second loop: EFGH

}

 

# Get database tables

sqlite.getTables # => ["TestTable"]

 

# Get table's columns

sqlite.getColumns("TestTable") # => ["C1", "C2"]

 

# Backup

sqlite.backup("NewDBFile.db") # Save all on "NewDBFile.db"

 

# Backup to another instance

tmpsql = SQLite.new("OtherDB.db")

tmpsql.getTables # => []

sqlite.backup(tmpsql)

tmpsql.getTables # => ["TestTable"]

 

# Restore backup

sqlite.restore("NewDBFile.db") # Copy all from "NewDBFile.db" to open instance

 

# Temporarily close

sqlite.tmpClose{

  print "Block running with db closed"

}

 

# Finalize

sqlite.finalize

Compatibility

- RPG Maker VX Ace
I'll improve compatibility with VX n XP next days. It's because some Ruby 1.9 methods.

Credits and Thanks

SQLite.org - DLL & API

Author's Notes

Any problem with this script feel free to contact me by pm or email (gab.teles@hotmail.com).

Terms and Conditions

- Free for commercial use. It only apply to script, look at SQLite Copyright to see about it's license.
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top