SQLite et les types de données

Pour le meilleur et pour le pire, SQLite est l’unique sgbd embarqué sur les principales plateformes mobiles du marché – au moins, il est présent sur toutes, c’est déjà ça.

Pour quelqu’un d’habitué à un sgbdr comme Oracle ou Postgresql, le système de typage de SQLite peut être très déroutant et entraîner des bogues très difficiles à débusquer. Cet article va vous donner quelques trucs pour éviter les plus vicieux.

La documentation ne vous prend pas en traître : les colonnes en SQLite ne sont pas typées statiquement (cf https://www.sqlite.org/datatype3.html). Eux ont l’air de trouver ça génial, vous allez voir que ce n’est pas vraiment mon avis.

Concrètement, qu’est-ce-que ça change ? Quand vous insérez une valeur dans une colonne SQLite, SQLite décide de lui-même d’une « classe de stockage » (storage class) qui peut être NULL, INTEGER, REAL (en virgule flottante), TEXT et BLOB suivant les caractéristiques qu’il perçoit de la donnée que vous lui transmettez.

Mais alors, si SQLite décide du stockage lui-même, à quoi servent les types qui sont données dans les instructions CREATE TABLE ? La réponse est que ce ne sont pas des types, mais des « affinités de types » (type affinities), une sorte de préférence pour le stockage. Pour les exemples ci-dessous, j’emploierai la table suivante :

CREATE TABLE TEST_CRAPPY_TYPING_SYSTEM(
  t text,
  not_quite_float float
);

La première conséquence est que de ligne en ligne, la classe de stockage pour une même colonne peut varier ; par exemple :

INSERT INTO TEST_CRAPPY_TYPING_SYSTEM VALUES('test',500); -- 500 est stocké en INTEGER
INSERT INTO TEST_CRAPPY_TYPING_SYSTEM VALUES('test 2',500.5); -- 500.5 est stocké en REAL

La deuxième conséquence, encore plus rigolote, est que contrairement à l’exemple ci-dessus, la classe peut sembler complètement incompatible :

INSERT INTO TEST_CRAPPY_TYPING_SYSTEM VALUES('test 3','tralala'); -- ne fait pas d'erreur et est stocké en TEXT

Ce qui peut amener si on ne fait pas attention à des cas très vicieux :

INSERT INTO TEST_CRAPPY_TYPING_SYSTEM VALUES('test 4','4,567'); -- stocké sous forme de chaîne, car SQLite ne reconnait pas 4,567 comme un REAL !

Cas particulièrement vicieux car si vous faites :

SELECT not_quite_float * 100 FROM TEST_CRAPPY_TYPING_SYSTEM WHERE t='test 4';

Vous recevez un joli 400 – car SQLite fait un gros effort pour tenter de faire de votre chaîne un nombre, qui s’arrête lamentablement à la virgule ; du coup, vous avez l’impression qu’il y a bien quelque chose comme un nombre dans votre valeur, sauf que non. Ca fait un peu penser à MySQL et son absence d’erreurs sur les débordements numériques, sauf que SQLite a l’excuse de le faire exprès et de le documenter.

Maintenant, si vous exécutez le code C# suivant (j’omets la création de la connexion et toutes les fermetures propres de ressources) :

IDdCommand cmd = conn.CreateCommand();
cmd.CommandText = "SELECT not_quite_float FROM TEST_CRAPPY_TYPING_SYSTEM";
IDataReader rs = cmd.ExecuteReader();
while(rs.Read()) {
  Console.WriteLine(rs.GetValue(0).GetType().FullName);
}

Vous récupérez Int32, Decimal, String, String. C’est un peu mieux que le driver OleDb Excel qui devine le type sur les premières lignes, puis renvoie NULL si il rencontre une valeur qui ne se conforme pas au type qu’il a décidé, mais il vaut mieux être prévenu.

Conclusion : si vous voulez de la cohérence de typage avec SQLite, do it yourself, et si vous avez des résultats étranges avec les types numériques, vérifiez bien ce que vous avez donné à manger à SQLite en insertion.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *