<Guide du Rootard GeeXLab/>

SQLite3


Dernière mise à jour: 2018.12.10 par JeGX

>> Retour <<




SQLite3 est un moteur de base de données SQL très utilisé et surtout, très pratique car il peut être embarqué dans n'importe quelle application (le célèbre navigateur Firefox utilise SQLite de manière intensive). De plus, chaque base de données SQLite3 réside entièrement dans un simple fichier.

Pour toutes ces raisons, le support SQLite3 a été ajouté à GeeXLab depuis la version 0.22.1.

SQLite3 peut être utilisé de trois façons: soit en utilisant la librairie gh_sqlite3 (disponible en Lua et Python), soit en utilisant LuaSQL (intégré à GeeXLab), soit en utilisant le module sqlite3 en Python (#import sqlite3).

Comment Utiliser gh_sqlite3 ?

Nous allons voir comment créer une petite base de données de test stockée dans un fichier (database01.db). Cette base contient une seule table (users) avec deux colonnes (name et age). Nous utiliserons des requêtes SQL pour manipuler la base de données. D'abord quelques constantes:

local SQLITE_OK = 0 
local SQLITE_ERROR = 1
local SQLITE_ROW = 100
local SQLITE_DONE = 101 
 
local SQLITE_INTEGER = 1
local SQLITE_FLOAT = 2
local SQLITE_TEXT = 3
local SQLITE_BLOB = 4


Commençons par créer le fichier de la base de données (ou simplement de l'ouvrir s'il existe déjà):

local demo_dir = gh_utils.get_demo_dir()
local db_filename = demo_dir .. "database01.db"
local rc = SQLITE_OK
dbid, rc = gh_sqlite3.db_open(db_filename)

dbid est l'identifiant de la base de données que nous utiliseront dans toutes les autres fonctions de gh_sqlite3.

Créons maintenant la table users:

local sql = "DROP TABLE users"
if (gh_sqlite3.db_exec(dbid, sql) == SQLITE_ERROR) then
  print("SQL error 1")
end  
 
sql = "CREATE TABLE users (name varchar(64), age int not null)"
if (gh_sqlite3.db_exec(dbid, sql) == SQLITE_ERROR) then
  print("SQL error 1")
end

Maintenant que la table users est créée, nous allons la remplir avec quelques enregistrements:

sql = "INSERT INTO users (name, age) VALUES ('toto', 12);"
if (gh_sqlite3.db_exec(dbid, sql) == SQLITE_ERROR) then
  print("SQL error 2")
end
 
sql = "INSERT INTO users (name, age) VALUES ('John', 53);"
if (gh_sqlite3.db_exec(dbid, sql) == SQLITE_ERROR) then
  print("SQL error 2")
end
 
sql = "INSERT INTO users (name, age) VALUES ('Viktor', 48);"
if (gh_sqlite3.db_exec(dbid, sql) == SQLITE_ERROR) then
  print("SQL error 2")
end
 
sql = "INSERT INTO users (name, age) VALUES ('Duke', 85);"
if (gh_sqlite3.db_exec(dbid, sql) == SQLITE_ERROR) then
  print("SQL error 2")
end

La base est maintenant opérationnelle. On va maintenant la lire avec une belle requête SELECT:

local sql = "SELECT * FROM users"
if (gh_sqlite3.db_prepare(dbid, sql) == SQLITE_OK) then
  local num_columns = gh_sqlite3.db_get_column_count(dbid)
  print("# columns: " .. num_columns)
 
  local row = 1
  rc = gh_sqlite3.db_step(dbid)
  while (rc ~= SQLITE_DONE) do
 
    if (rc == SQLITE_ROW) then
      for c=0, num_columns-1 do
 
        local type = gh_sqlite3.db_get_column_type(dbid, c)
        if (type == SQLITE_TEXT) then
          local v = gh_sqlite3.db_column_get_text(dbid, c)
          print("Col. " .. c .. " | TEXT | " .. v)
        elseif  (type == SQLITE_INTEGER) then
          local v = gh_sqlite3.db_column_get_int(dbid, c)
          print("Col. " .. c .. " | INT | " .. v)
        elseif  (type == SQLITE_FLOAT) then
          local v = gh_sqlite3.db_column_get_double(dbid, c)
          print("Col. " .. c .. " | REAL | " .. v)
        elseif  (type == SQLITE_BLOB) then
          local blob_ptr, blob_size = gh_sqlite3.db_column_get_blob(dbid, c)
          print("Col. " .. c .. " | BLOB | " .. blob_size)
        end
      end
    end
 
    -- next result.
    rc = gh_sqlite3.db_step(dbid)
 
    if ((rc == SQLITE_ERROR) or (rc == SQLITE_DONE)) then
      break
    end
  end
 
  gh_sqlite3.db_finalize(dbid)
end

Quand la base de données n'est plus nécessaire, on peut la fermer proprement avec:

gh_sqlite3.db_close(dbid)


Le champ de type BLOB mérite de s'y intéresser un peu plus. Un champ BLOB permet de stocker n'importe quel type de données. On peut par exemple, y stocker le contenu d'un fichier image.

Voyons rapidement comment stocker une image .jpg dans un champ blob. Nous allons créer un table images contenant 3 champs: id, name et data. Nous allons ensuite insérer une image jpg dans le champ blob.

local sql = "CREATE TABLE images (id INTEGER PRIMARY KEY, name varchar(64), data BLOB)"
if (gh_sqlite3.db_exec(dbid, sql) == SQLITE_ERROR) then
	print("SQL error 1")
end  

sql = "INSERT INTO images (name, data) VALUES (?, ?);"
if (gh_sqlite3.db_prepare(dbid, sql) == SQLITE_OK) then

	local column = 1
	gh_sqlite3.db_bind_text(dbid, column, "image.jpg") -- Image name

	column = 2
	gh_sqlite3.db_bind_blob_from_file(dbid, column, demo_dir .. "assets/image.jpg") -- Image data

	rc = gh_sqlite3.db_step(dbid)

	gh_sqlite3.db_finalize(dbid)
end


La fonction db_bind_blob_from_file() fait tout le travail: elle permet très simplement de stocker le contenu de n'importe quel fichier dans un champ blob.

On pourrait aussi utiliser les fonctions de manipulations des buffers à la place de db_bind_blob_from_file:

sql = "INSERT INTO images (name, data) VALUES (?, ?);"
if (gh_sqlite3.db_prepare(dbid, sql) == SQLITE_OK) then

	local column = 1
	gh_sqlite3.db_bind_text(dbid, column, "image.jpg") -- Image name

	buffer, buffer_size = gh_utils.file_buffer_create(demo_dir .. "assets/image.jpg")
  
	column = 2
	gh_sqlite3.db_bind_blob_from_buffer(dbid, column, buffer, buffer_size)

	rc = gh_sqlite3.db_step(dbid)

	gh_sqlite3.db_finalize(dbid)

	gh_utils.file_buffer_kill(buffer)
end


On peut ensuite créer une texture à partir du champ blob précédant avec la fonction create_from_buffer de la librairie gh_texture:


local buffer, buffer_size = gh_sqlite3.db_column_get_blob(dbid, c)

upload_to_gpu = 1
pixel_format = U8_RGBA
texture_unit = 0
create_srv = 0 -- for d3d12.
gen_mipmaps = 1
compressed_format = ""
free_cpu_memory = 1

gh_texture.create_from_buffer(buffer, buffer_size, upload_to_gpu, pixel_format, texture_unit, create_srv, gen_mipmaps, compressed_format, free_cpu_memory)




Guide du Rootard GeeXLab | Téléchargements | Contact | Newsletter