dimanche 23 avril 2023
Quelques expérimentations avec SQLite et sqlite3
notamment une extension avec une fonction de chaines de caractères écrite en C.
SQLite est une bibliothèque écrite en C. Voir page wikipedia.
Le paquet sqlite founit en plus de la librairie une interface (CLI) rudimentaire sqlite3
. La documentation hors ligne est dans le paquet sqlite-doc.
La particularité de SQLite est d'encapsuler dans un seul fichier l'intégralité d'une base de donnée sans passer par un schéma client-serveur.
Exemple de session sqlite3 :
$ sqlite3 exemple.db
SQLite version 3.41.2 2023-03-22 11:56:21
Enter ".help" for usage hints.
sqlite> create table t(j TEXT, h TEXT, objet TEXT);
sqlite> .schema t
CREATE TABLE t(j TEXT, h TEXT, objet TEXT);
sqlite> insert into t(j, h, objet)
values ('2023-04-23','07:00', 'lever'),
('2023-04-23', '10:15', 'grognon'),
('2023-04-23', '23:00', 'dodo');
sqlite> select * from t;
2023-04-23|07:00|lever
2023-04-23|10:15|grognon
2023-04-23|23:00|dodo
sqlite> .quit
Ligne 1 : Ouverture d'une session sqlite3 avec le fichier example.db. Si ce dernier n'existe pas il est crée.
Ligne 4 : (sql) Création d'une table nommée t. Le type de donnée TEXT est précisé mais on pourrait l'omettre. SQLite utilise les classes de stockage NULL, INTEGER, REAL, TEXT, BLOB. Mais SQLite a un typage automatique et flexible. Voir Datatypes In SQLite.
Ligne 5 : (méta commande) Affiche le résultat de la déclaration de la table.
Lignes 7 à 10 : (sql) Ajoute trois lignes à la table t.
Ligne 11 : (sql) requête classique.
Lignes 12 à 14 : résultat de la requête.
Ligne 15 : (méta commande) fin de la session.
Remarque : SQLite accepte les mots clé sql en minuscules. Il les traduit dans la ligne 6 en majuscules.
man sqlite3
fournit de façon sommaire les méta-commandes de l'interface.
Le langage sql est assez standard, nonobstant le typage particulier et implicite de SQLite.
Ici je présente une tentative d'une extension de SQLite : une fonction le
qui complète la saisie d'une date par les valeurs courantes.
Code:
/* le.c
:w | !gcc -g -fPIC -shared % -o %<.so
(c) 2023 Mourad Arnout marnout à free.fr
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <time.h>
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
/* le('french date') input french date format
Let us say the current month is '04' and the current year is '2023'
le('12') -> '2023-04-12'
le('6/5') -> '2023-05-06'
le('1/2/2024') -> '2024-02-01'
*/
static void le(sqlite3_context *context, int argc, sqlite3_value **argv)
{
assert(argc==1);
char *s = (char *)sqlite3_value_text(argv[0]); // in string
char buf[32]; // out string
time_t t = time(NULL); // time now
struct tm *tm;
tm = localtime(&t); // current local time
int n, d, m, y;
n = sscanf(s, "%d/%d/%d", &d, &m, &y);
if(n > 0) tm->tm_mday = d;
if(n > 1) tm->tm_mon = m - 1;
if(n > 2) tm->tm_year = y - 1900;
strftime(buf, sizeof(buf), "%Y-%m-%d", tm);
sqlite3_result_text(context, buf, -1, SQLITE_TRANSIENT);
}
int sqlite3_le_init(sqlite3 *db, char **pzErrMsg,
const sqlite3_api_routines *pApi)
{
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "le", 1, SQLITE_UTF8, 0, le, 0, 0);
return rc;
}
Compilation : gcc -g -fPIC -shared % -o %<.so
.
Le but de cette fonction est de faciliter la saisie d'une date dans sqlite3
.
L'argument est chaine de caractères au format 'j', 'j/m' ou 'j/m/a', où j est le jour du mois (1 .. 28, 29, 30 ou 31), m le mois (1 .. 12) et a est l'année (4 chiffres). Le résultat est une date au format sql.
Exemple :
Admettons qu'on soit le 23 avril 2023 (date de rédaction de cet article).
le('24')
renvoie '2023-04-24'
le('12/9')
renvoie '2023-09-12'
le('4/1/2024') renvoie '2024-01-04'.
Je me suis inspiré de la fonction rot13 donnée en exemple dans la documentation rot13.c. Je ne comprends pas tout là dedans. Exemple la ligne 45, que j'ai gardée à défaut d'en saisir l'utilité.
La documentation disponible Obtaining SQL Values et Setting The Result Of An SQL Function est assez imbuvable.
Pour la ligne 46 voir Create Or Redefine SQL Functions.
Les lignes 27 à 36 sont du C classique.
Exemple d'utilisation avec la base définie au paragraphe 1 :
$ sqlite3 exemple.db
SQLite version 3.41.2 2023-03-22 11:56:21
Enter ".help" for usage hints.
sqlite> .load lib/le.so
sqlite> insert into t(j, h, objet) values(le(''), '16:00', 'mise en ligne');
sqlite> insert into t(j, h, objet) values(le('24'), '11:20', 'repos');
sqlite> .mode box
sqlite> select * from t;
┌────────────┬───────┬───────────────┐
│ j │ h │ objet │
├────────────┼───────┼───────────────┤
│ 2023-04-23 │ 07:00 │ lever │
│ 2023-04-23 │ 10:15 │ grognon │
│ 2023-04-23 │ 23:00 │ dodo │
│ 2023-04-23 │ 16:00 │ mise en ligne │
│ 2023-04-24 │ 11:20 │ repos │
└────────────┴───────┴───────────────┘
sqlite>
Ligne 1 : ouverture de session avec la base du paragraphe 1
Ligne 4 : (meta-command) chargement de la librairie lib/le.so
.
Ligne 5 : (sql) ajout d'une ligne, le paramètre '' de la fonction le étant vide, c'est la date courante (aujourd'hui: 23/4/2023).
Ligne 6 : (sql) ajout d'une ligne, le paramètre '24' de le est le jour du mois courant (04) de l'année courante (2023).
Ligne 7 : (meta-command) mode d'affichage tableau
Ligne 8 : (sql) requête ordinaire.
Lignes 9 à 17 : output de sqlite3