Publi

Usando SQLite en nuestros programas en C/C++ (I)


Foto: Eirik Stavelin (Flickr CC-by)
A menudo, nuestros programas necesitan almacenar información (temporal o no) de forma ordenada, rápida y que no nos complique la vida. Luego también necesitamos poder acceder a ella con la misma facilidad. Para eso vale SQLite. Tendremos un pequeño motor de base de datos que con sólo un par de archivos (.h y .c) más un archivo de datos lo tendremos todo listo.

Una pequeña introducción

SQLite nos proporciona una forma muy sencilla de introducir y eliminar información (si estamos familiarizados con el lenguaje SQL) sin las complicaciones de tener un motor de base de datos corriendo (MySQL, MariaDB, PostreSQL, MSSQL…). Por un lado, al no realizar conexiones, todo debería ir mucho más rápido, en bases de datos relativamente pequeñas se nota. Además, no podremos hacer llamadas de forma remota (como hemos hecho siempre, conectando con el gestor de base de datos), ni tampoco podremos montar clusters ni nada de eso (directamente, seguro que hay algún proyecto por ahí que lo permita). Las instrucciones SQL soportadas no son muchas (comparado con motores grandes) pero en muchísimos casos tendremos suficiente.

¿Quién usa SQLite?

En un primer momento, podríamos no adoptar una tecnología que no esté ampliamente aceptada (es normal, en cualquier momento es desatendida y ¡todo nuestro código a la basura!), y una tecnología que no sea libre (sobre todo para poder estudiarla, para ver que no tenga una cara oculta). Éstas son dos condiciones que pongo personalmente cuando empiezo a trabajar con alguna.
SQLite se usa en muchos programas como Skype, algunos programas de Adobe, Firefox, Chrome, Safari y muchos más. Además, tenemos extensiones para SQLite en muchos lenguajes de programación como Java, Python, PHP, y muchísimos más (Wikipedia)

¿Dónde me bajo lo necesario?

Directamente en la web oficial: SQLite download. Yo siempre descargo sqlite-amalgamation, aquí tenemos todo el sistema sqlite en dos archivos sqlite.h y sqlite.c listos para trabajar.
Si utilizas GNU/Linux, lo más probable es que tu distribución tenga un paquete sqlite y otro sqlite-dev con el código fuente (necesitaremos los dos).

Para los ejemplos cuento con que tenemos sqlite3.h en el mismo directorio que el código de nuestro programa.

Compilar el código

Todos los códigos que pondré en el post se compilan de la misma manera con gcc. Aunque tenemos dos posibilidades. Por un lado podemos incluir sqlite en nuestro ejecutable. Nuestro programa pesará más, pero no será necesario tener la biblioteca instalada. Eso sí, para compilar, necesitamos el archivo sqlite3.c de la amalgama de código de sqlite. Esto lo haremos así:

$ gcc -o ejecutable fuente.c sqlite3.c -ldl -lpthread

(Si usas Windows, necesitarás otras bibliotecas diferentes de dl y phtread).

Por otro lado, si queremos aprovechar la biblioteca dinámica de sqlite3 instalada en nuestro sistema (ya que muchas aplicaciones lo utilizan, ahorramos cerca de 1Mb de código en cada ejecutable), lo podemos hacer de la siguiente manera:

$ gcc -o ejecutable fuente.c -lsqlite3

De esta forma, los ejecutables ocuparán muchísimo menos, pero necesitaremos tener SQLite instalado en nuestro sistema.

Hola mundo – Sacando la versión del motor

Como primer programa, antes de hacer nada con la base de datos, vamos a consultar la versión de SQLite que tenemos. Por ejemplo, si utilizamos la versión de SQLite instalada en el sistema es muy interesante, porque puede que nosotros estemos utilizando características propias de una versión de la biblioteca y debemos asegurarnos de que la versión que tiene el usuario es igual o posterior.

sqversion.c

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"

int main(int argc, char* argv[])
{
   printf ("SQLITE LIB Version: %s\n", sqlite3_libversion());
   printf ("SQLITE LIB Version (int): %d\n", sqlite3_libversion_number());

   if (sqlite3_libversion_number()<3001008)
     printf ("Lo siento, tu versión de SQLite es muy antigua\n");
}

En este caso, para visualizar la versión, nos viene bien la forma bonita, a modo de cadena de caracteres, con sus puntos y todo; pero cuando queremos realizar la comparación, nos será mucho más fácil utilizar la forma numérica.

Abriendo y cerrando la base de datos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"

int main(int argc, char* argv[])
{
   sqlite3 *db;
   int res;

   /* Open database */
   res = sqlite3_open("test.db", &db);
   if (res)
   {
      fprintf(stderr, "No puedo abrir la base de datos: %s\n", sqlite3_errmsg(db));
      exit(0);
   }
   else
   {
      fprintf(stderr, "Base de datos OK\n");
   }

   sqlite3_close(db);

   return 0;
}

Este pequeño programa no hace nada… bueno sólo abre y cierra la base de datos, que normalmente se hará con éxito. Si queremos probar un caso en el que falle, así rápidamente, podemos quitar los permisos de lectura:

$ chmod -r test.db

al archivo test.db para que veamos que también puede fallar el programa.

Nuestra primera consulta : Creando una tabla

Podemos usar este pequeño código para empezar creando una tabla. En este ejemplo, vamos a hacer un pequeño log de eventos de nuestro programa, en el que podemos almacenar la marca de tiempo, nivel (si es más o menos crítico), tipo (otro número), y mensaje.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"

int main(int argc, char* argv[])
{
   sqlite3 *db;
   char *error = 0;
   int res;
   char *sql;

   /* Open database */
   res = sqlite3_open("test.db", &db);
   if (res)
     {
       fprintf(stderr, "No puedo abrir la base de datos: %s\n", sqlite3_errmsg(db));
       exit(0);
     }
   else
     {
       fprintf(stderr, "Base de datos OK\n");
     }
   /* Create SQL statement */
   sql = "CREATE TABLE events ("
     "`timestamp` DATETIME, "
     "`level` NUMBER, "
     "`type` NUMBER, "
     "`message` TEXT)";

   /* Execute SQL statement */
   res = sqlite3_exec(db, sql, NULL, 0, &error);
   if (res != SQLITE_OK)
     {
       fprintf(stderr, "Error: %s\n", error);
       sqlite3_free(error);
     }
   else
     {
       fprintf(stdout, "Tabla creada!\n");
     }

   sqlite3_close(db);

   return 0;
}

De nuevo, si lo ejecutamos, nos dirá que la tabla ha sido creada, y saldrá del programa.

Insertando información en la tabla

Para ello, vamos a cambiar el SQL por lo siguiente:

1
2
3
4
5
6
7
8
   /* STRFTIME('%s','now') - Unix timestamp
    DATETIME(STRFTIME('%s','now')) = DATETIME('now') but we can operate with
       the timestamp
*/

   sql = "INSERT INTO events VALUES (DATETIME(STRFTIME('%s','now'), 'unixepoch'), 1, 2, 'This is a test');"
     "INSERT INTO events VALUES (DATETIME(STRFTIME('%s','now')+86400, 'unixepoch'), 10, 4, 'This is a test again');"
     "INSERT INTO events VALUES (DATETIME(STRFTIME('%s','now')+86400*2, 'unixepoch'), 100, 8, 'This is a test again x2');"
     "INSERT INTO events VALUES (DATETIME(STRFTIME('%s','now')+86400*20, 'unixepoch'), 1000, 16, 'This is a test again x3');";

Hemos insertado cuatro filas de datos, en las que rellenamos la fecha y hora, ponemos valores numéricos en level y type y un mensaje final.
Me he complicado la vida un poco para poner la fecha y hora, más que nada para que se vayan sumando algunos días y las fechas sean diferentes.

Obteniendo datos de la tabla

Ahora el tema va a ser ligeramente diferente, porque vamos a extraer información en lugar de generarla y, en muchos casos, puede ser gran cantidad de información la que estamos extrayendo. En este caso, cuando llamamos a la función sqlite3_exec(), el tercer parámetro que hasta ahora es NULL, va a ser el nombre de una función de callback encargada de recibir los datos por parte de SQLite, y ya, nosotros veremos lo que hacemos con ellos. La forma de pasar los datos, será parecida a cómo recibe los argumentos (desde la función main()) un programa en C o C++, gracias a dos variables: una que devuelve el número de argumentos recibidos (argc) y otra que recibe el contenido de éstos (argv). Además, tendremos otra variable más que recibirá los nombres de los campos. Esta función será llamada a cada fila recibida.

Podemos hacer algo como esto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>
#include <stdlib.h>
#include "sqlite3.h"

static int selectCb(void *nada, int argc, char **argv, char **colNames){
   int i;

   for(i=0; i<argc; i++){
      printf("%s => %s\n", colNames[i], argv[i]);
   }
   printf("\n");
   return 0;
}

int main(int argc, char* argv[])
{
   sqlite3 *db;
   char *error = 0;
   int res;
   char *sql;

   /* Open database */
   res = sqlite3_open("test.db", &db);
   if (res)
     {
       fprintf(stderr, "No puedo abrir la base de datos: %s\n", sqlite3_errmsg(db));
       exit(0);
     }
   else
     {
       fprintf(stderr, "Base de datos OK\n");
     }

   /* Create SQL statement */
  sql = "SELECT * FROM events;";

   /* Execute SQL statement */
  res = sqlite3_exec(db, sql, selectCb, 0, &error);
   if (res != SQLITE_OK)
     {
       fprintf(stderr, "Error: %s\n", error);
       sqlite3_free(error);
     }
   else
     {
       fprintf(stdout, "SELECT Ok!\n");
     }
   sqlite3_close(db);

   return

En este caso tendremos una salida completa con todos los valores de la tabla, aunque bien podemos usar WHERE, LIMIT, etc.

Tenemos un argumento curioso para sqlite3_exec(), que directamente he colocado como 0 en todas las peticiones, ese valor será una variable que podemos pasar al callback cada vez que se ejecute, y que puede tomar el valor que queramos (y también recibir, que para eso es un puntero), lo que significa que tenemos muchas más posibilidades, como poner un identificador para decidir qué hacer con la información recibida o crear otra estructura basada en listas enlazadas para almacenar todo el SELECT y poder leerlo tras ejecutar sqlite3_exec() si todo ha ido bien.

Una pequeña buena práctica

Es bueno utilizar llamadas a sqlite3_initialize() cuando vamos a empezar a utilizar la biblioteca. Y a sqlite3_shutdown() cuando terminamos de utilizarla y no vamos a hacerlo más. Es verdad que no hace realmente falta, pero podemos compilar especificando el define SQLITE_OMIT_AUTOINIT, como su nombre indica, no auto-inicializará. No ganaremos excesiva velocidad no inicializando porque sqlite3_initialize() cuando ya está el sistema inicializado no hará nada, pero son llamadas y comprobaciones que podemos hacer y si nuestra aplicación exige mucho uso de SQLite o el sistema es modesto, seguro que se agradece.

También podría interesarte....

There are 27 comments left Ir a comentario

  1. Pingback: Usando SQLite en nuestros programas en C/C++ (I) | PlanetaLibre /

  2. Pingback: Usando prepared statements en SQLite en C/C++ /

  3. Manu /
    Usando Mozilla Firefox Mozilla Firefox 50.0 en Windows Windows NT

    En mi programa en C estoy usando esta sentencia:
    «SELECT * FROM test WHERE idreg BETWEEN ? AND ? ORDER BY ?;»
    Los dos primeros parámetros «idreg BETWEEN ? AND ?» funcionan perfectamente pero el tercero «ORDER BY ?» pasa de él, como si no existiera.
    He hecho varias pruebas y no lo consigo… Los tres parámetros les asigno el valor con las funciones sqlite3_bind_…().

    Qué estoy haciendo mal?

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 50.0 en Ubuntu Linux Ubuntu Linux

      Hola Manu, hasta donde yo sé, al ORDER BY no le puedes poner una ? porque SQLite no es capaz de compilar la sentencia. Precisamente ese es uno de los sitios que no le gustan a SQLite.

      Felices fiestas !

  4. Manu /
    Usando Mozilla Firefox Mozilla Firefox 50.0 en Windows Windows NT

    Por cierto, Feliz Navidad

  5. Manu /
    Usando Mozilla Firefox Mozilla Firefox 50.0 en Windows Windows NT

    Gracias Gaspar, felices fiestas…
    Ya me parecía a mi. En MySQL si se puede.
    Yo si puedo compilar la sentencia pero siempre me la ordena por el orden natural (rowid). Por favor me puedes dar un enlace donde se explique eso?

    Lo dicho felices fiestas y gracia por contestar 😉

  6. Alex /
    Usando Mozilla Firefox Mozilla Firefox 58.0 en Windows Windows NT

    Hola, mis felcitaciones por el articulo, es lo que estaba necesitando.

    Estoy iniciando con C++ y me veo en la necesidad de crear una «dll» que interactue con mi aplicación, he podido llevar acabo este proyecto y tu articulo me guio para ese.

    En lo unico que me estoy estancando es que no se como recuperar los valores de una busqueda. Me seria de gran ayuda que me des algunas recomendaciones.

    Desde ya muy agradecido y te animo a que sigas con este tipo de articulos que contribuyen de gran manera.

    Atentamente.

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 57.0 en Linux Linux

      Gracias por tu comentario Alex. Puede que te pueda ayudar la segunda parte del post que encuentras aquí: https://poesiabinaria.net/2015/04/usando-sqlite-en-nuestros-programas-en-cc-ii-nueva-interfaz-v2-y-prepared-statements/

      ¡Ya me vas contando! Un cordial saludo!

  7. Luke Skywalker Yellow Jacket /
    Usando Google Chrome Google Chrome 119.0.0.0 en Windows Windows NT

    Good step you have written of writing content relating to cooperate sense. Better yet good working skills and hope you write more of this soon.

  8. Andrew Mark /
    Usando Google Chrome Google Chrome 119.0.0.0 en Windows Windows NT

    This is excellent article, thank you for the share! This is what I am looking for, hope in future you will continue sharing such an superb work.
    Top Gun Jacket UK

  9. yitzchak kerrigan /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Literally Document look at the software yesterday evening still I had produced a lot of emotions relating to this now I needed to read the software once as it is relatively well crafted. chatbots

  10. yitzchak kerrigan /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Step into the enchanting world of movies123, where cinematic wonders await at every turn. Each film on 123movies is a magical journey, offering a diverse tapestry of genres for an unforgettable movie night at home. Immerse yourself in the captivating allure of Movies123, your premier destination for cinematic excellence.

  11. yitzchak kerrigan /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    I’ve got not long ago started off some sort of blog site, the internet people produce here possesses served everyone enormously. Appreciate it intended for all of your current time period & do the job. fire kirin login

  12. yitzchak kerrigan /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Wonderful article. The actual publish impacts lots of immediate problems in our culture. All of us cannot be uninvolved in order to these types of problems. This particular publish provides plans as well as ideas. Really educational as well as useful. libri interessanti 2024

  13. yitzchak kerrigan /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Regards for the purpose of post this amazing piece of writing! I recently came across yuor web blog perfect for your preferences. It includes marvelous not to mention advantageous items. Cultivate monetary management give good results! https://bestbeachreviews.com

  14. ghori92 /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Great post, and great website. Thanks for the information! изработка на сайтове

  15. ghori92 /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Most of the time I don’t make comments on websites, but I’d like to say that this article really forced me to do so. Really nice post! CNC Milling China

  16. seooo servicesss /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    I know this is one of the most meaningful information for me. And I’m animated reading your article. But should remark on some general things, the website style is perfect; the articles are great. Thanks for the ton of tangible and attainable help. 더킹플러스카지노

  17. MUZAMMIL SEO MUZAMMIL SEO /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Succeed! It could be one of the most useful blogs we have ever come across on the subject. Excellent info! I’m also an expert in this topic so I can understand your effort very well. Thanks for the huge help. 더킹플러스카지노

  18. MUZAMMIL SEO MUZAMMIL SEO /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    I am happy to find your distinguished way of writing the post. Now you make it easy for me to understand and implement the concept. Thank you for the post. 춘천 스웨디시

  19. Movies joy /
    Usando Google Chrome Google Chrome 109.0.0.0 en Windows Windows NT

    moviesjoy has a wide-ranging library of movies and TV shows, ranging from classic films to the latest blockbusters. Whether you are a fan of action, romance, comedy, thriller, or any other genre, this platform has got you covered. Additionally, the availability of TV shows allows binge-watchers to immerse themselves in their favorite series.

  20. MUZAMMIL SEO MUZAMMIL SEO /
    Usando Google Chrome Google Chrome 121.0.0.0 en Windows Windows NT

    My spouse and i shocked while using investigation anyone created to choose this certain release outstanding. Amazing task! Visa de Canadá en línea desde Bulgaria

  21. ghori92 /
    Usando Google Chrome Google Chrome 121.0.0.0 en Windows Windows NT

    nice bLog! its interesting. thank you for sharing…. private gym Casselberry fl

  22. MUZAMMIL SEO MUZAMMIL SEO /
    Usando Google Chrome Google Chrome 121.0.0.0 en Windows Windows NT

    Cool stuff you have got and you keep update all of us. milky way login

  23. ALEXENDER RAVI /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    Immerse yourself in the world of online gaming with confidence on our recommended Toto and casino sites. Receive free money, accumulate bonus points, and exchange them for real cash without the risk of scams. Enjoy a secure and thrilling gaming experience. 카지노 꽁머니

  24. seooo servicesss /
    Usando Google Chrome Google Chrome 121.0.0.0 en Windows Windows NT

    This type of message always inspiring and I prefer to read quality content, so happy to find good place to many here in the post, the writing is just great, thanks for the post. Turkiets ambassad i Moçambique

  25. MUZAMMIL SEO MUZAMMIL SEO /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    Thanks for taking the time to discuss this, I feel strongly about it and love learning more on this topic. If possible, as you gain expertise, would you mind updating your blog with extra information? It is extremely helpful for me. loafsmagazine

Leave a Reply