1. Introduction2. Le VFS en relation avec le reste de SQLite3. VFS multiples3.1. VFS standard Unix3.2. VFS standard de Windows3.3. Spécifier le VFS à utiliser3.4. VFS Shims3.5. Autres exemples de VFS4. Implémentations VFS

Cet article décrit la couche de portabilité SQLite OS ou "VFS" - le module au bas de la pile d'implémentation SQLite qui fournit la portabilité à travers les systèmes d'exploitation.

L'organisation interne de la bibliothèque SQLite peut être vue comme la pile de modules présentée à droite. Les composants Tokenizer, Parser, et Code Generator sont utilisés pour traiter les déclarations SQL et les convertir en programmes exécutables dans un langage de machine virtuelle ou en code d'octet. Grosso modo, ces trois couches supérieures mettent en œuvre sqlite3_prepare_v2(). Le code à octets généré par les trois couches supérieures est une instruction préparée. Le module Virtual Machine est responsable de l'exécution du code à barres de l'instruction SQL. Le module B-Tree organise un fichier de base de données en plusieurs magasins clés/valeurs avec des clés ordonnées et des performances logarithmiques. Le module Pager est responsable du chargement des pages du fichier de base de données en mémoire, de la mise en œuvre et du contrôle des transactions, ainsi que de la création et de la maintenance des fichiers journaux qui empêchent la corruption de la base de données à la suite d'un crash ou d'une panne de courant. L'interface OS est une abstraction fine qui fournit un ensemble commun de routines pour adapter SQLite afin qu'il fonctionne sur différents systèmes d'exploitation. Grosso modo, les quatre couches inférieures mettent en œuvre sqlite3_step().

Cet article porte sur la couche inférieure.

L'interface OS - également appelée "VFS" - est ce qui rend SQLite portable sur tous les systèmes d'exploitation. Chaque fois qu'un des autres modules de SQLite a besoin de communiquer avec le système d'exploitation, il invoque des méthodes dans le VFS. Le VFS invoque alors le code spécifique au système d'exploitation nécessaire pour satisfaire la demande. Par conséquent, le portage de SQLite sur un nouveau système d'exploitation est simplement une question d'écriture d'une nouvelle couche d'interface OS ou "VFS".

L'arbre source standard de SQLite contient des VFS intégrés pour unix et windows. Des VFS alternatifs peuvent être ajoutés au démarrage ou à l'exécution en utilisant l'interface sqlite3_vfs_register().

Plusieurs VFS peuvent être enregistrés en même temps. Chaque VFS a un nom unique. Des connexions de base de données distinctes au sein d'un même processus peuvent utiliser différents VFS en même temps. D'ailleurs, si une seule connexion de base de données a plusieurs fichiers de base de données ouverts en utilisant la commande ATTACH, alors chaque base de données attachée pourrait utiliser un VFS différent.

3.1. VFS standard d'Unix

Les constructions Unix sont livrées avec plusieurs VFS intégrés. Le VFS par défaut pour unix est appelé "unix" et est utilisé dans la plupart des applications. Les autres VFS qui pourraient être trouvés dans unix (selon les options de compilation) comprennent :

  1. unix-dotfile - utilise le verrouillage de fichier par points plutôt que les verrous consultatifs POSIX.

  2. unix-excl - obtient et maintient un verrou exclusif sur les fichiers de la base de données, empêchant les autres processus d'accéder à la base de données. Garde également le wal-index dans le tas plutôt que dans la mémoire partagée.

  3. unix-none - toutes les opérations de verrouillage de fichiers sont no-ops.

  4. unix-namesem - utilise des sémaphores nommés pour le verrouillage des fichiers. VXWorks uniquement.

Les différents VFS unix ne diffèrent que par la façon dont ils gèrent le verrouillage des fichiers - ils partagent la plupart de leur implémentation en commun les uns avec les autres et sont tous situés dans le même fichier source SQLite : os_unix.c. Notez qu'à l'exception de "unix" et "unix-excl", les différents VFS unix utilisent tous des implémentations de verrouillage incompatibles. Si deux processus accèdent à la même base de données SQLite en utilisant différents VFS unix, ils peuvent ne pas voir les verrous des autres et finir par interférer l'un avec l'autre, ce qui entraîne une corruption de la base de données. Le VFS "unix-none", en particulier, n'effectue aucun verrouillage et entraînera facilement une corruption de la base de données s'il est utilisé par deux ou plusieurs connexions de base de données en même temps. Les programmeurs sont encouragés à utiliser uniquement "unix" ou "unix-excl", à moins qu'il n'y ait une raison impérieuse de faire autrement.

3.2. VFS standard de Windows

Les constructions Windows sont également livrées avec plusieurs VFS intégrés. Le VFS par défaut de Windows est appelé "win32" et est utilisé dans la plupart des applications. D'autres VFS qui pourraient être trouvés sur les builds de Windows incluent :

  1. win32-longpath - comme "win32", sauf que les noms de chemin peuvent avoir une longueur allant jusqu'à 65534 octets, alors que les noms de chemin plafonnent à 1040 octets dans "win32".

  2. win32-none - toutes les opérations de verrouillage de fichiers sont des no-ops.

  3. win32-longpath-none - combinaison de "win32-longpath" et "win32-none" - les noms de chemin longs sont supportés et toutes les opérations de verrouillage sont no-ops.

Comme avec unix, la plupart du code pour les différents VFS de Windows est partagé.

3.3. Spécifier quel VFS utiliser

Il y a toujours un VFS qui est le VFS par défaut. Sur les systèmes unix, le VFS "unix" apparaît comme le VFS par défaut et sur Windows, c'est "win32". Si aucune autre action n'est prise, les nouvelles connexions à la base de données utiliseront le VFS par défaut.

Le VFS par défaut peut être modifié en enregistrant ou réenregistrant le VFS à l'aide de l'interface sqlite3_vfs_register() avec un second paramètre de 1. Par conséquent, si un processus (unix) veut toujours utiliser le VFS "unix-nolock" à la place de "unix", le code suivant fonctionnerait :

sqlite3_vfs_register(sqlite3_vfs_find("unix-nolock"),1);

Un VFS alternatif peut également être spécifié comme 4ème paramètre de la fonction sqlite3_open_v2(). Par exemple :

int rc = sqlite3_open_v2("demo.db",&db, SQLITE_OPEN_READWRITE,"unix-nolock");

Enfin, si les noms de fichiers URI ont été activés, alors le VFS alternatif peut être spécifié en utilisant le paramètre "vfs=" sur l'URI. Cette technique fonctionne avec sqlite3_open(), sqlite3_open16(), sqlite3_open_v2(), et lorsqu'une nouvelle base de données est ATTACHÉE à une connexion de base de données existante. Par exemple :

ATTACH 'file:demo2.db?vfs=unix-none'AS demo2;

Le VFS spécifié par une URI a la plus haute priorité. Après cela vient un VFS spécifié comme quatrième argument de sqlite3_open_v2(). Le VFS par défaut est utilisé si aucun VFS n'est spécifié par ailleurs.

3.4. Cales VFS

Du point de vue des couches supérieures de la pile SQLite, chaque fichier de base de données ouvert utilise exactement un VFS. Mais en pratique, un VFS particulier pourrait n'être qu'une fine enveloppe autour d'un autre VFS qui fait le vrai travail. Nous appelons un VFS enveloppant un "shim".

Un exemple simple de shim est le VFS "vfstrace". Il s'agit d'un VFS (implémenté dans le test_vfstrace.c fichier source) qui écrit un message associé à chaque appel de méthode VFS dans un fichier journal, puis passe le contrôle à un autre VFS pour faire le travail réel.

3.5. Autres exemples de VFS

Voici d'autres implémentations de VFS disponibles dans l'arbre source public de SQLite :

  • appendvfs.c - Ce VFS permet à une base de données SQLite d'être annexée à la fin d'un autre fichier. Cela peut être utilisé, par exemple, pour annexer une base de données SQLite à la fin d'un exécutable de telle sorte que l'exécutable où elle se trouve puisse ensuite être facilement localisé par l'exécutable en cas de besoin. Le shell en ligne de commande utilisera ce VFS s'il est lancé avec l'option --append.

  • test_demovfs.c - Ce fichier implémente un VFS très simple nommé "demo" qui utilise des fonctions POSIX telles que open(), read(), write(), fsync(), close(), fsync(), sleep(), time(), et ainsi de suite. Ce VFS ne fonctionne que sur les systèmes unix. Mais il n'est pas destiné à remplacer le VFS standard "unix" utilisé par défaut sur les plateformes unix. Le VFS "demo" est délibérément maintenu très simple afin qu'il puisse être utilisé comme une aide à l'apprentissage ou comme modèle pour construire d'autres VFS ou pour porter SQLite sur de nouveaux systèmes d'exploitation.

  • test_quota.c - Ce fichier implémente une shim appelée "quota" qui applique des limites de taille de fichier cumulatives sur une collection de fichiers de base de données. Une interface auxiliaire est utilisée pour définir des "groupes de quotas". Un groupe de quotas est un ensemble de fichiers (fichiers de base de données, journaux et fichiers temporaires) dont les noms correspondent tous à un motif GLOB. La somme des tailles de tous les fichiers de chaque groupe de quotas est suivie, et si cette somme dépasse un seuil défini pour le groupe de quotas, une fonction de rappel est invoquée. Ce rappel peut soit augmenter le seuil, soit faire échouer l'opération qui aurait dépassé le quota avec une erreur SQLITE_FULL. Une des utilisations de cette cale est utilisée pour faire respecter les limites de ressources sur les bases de données des applications dans Firefox.

  • test_multiplex.c - Ce fichier implémente un shim qui permet aux fichiers de base de données de dépasser la taille maximale des fichiers du système de fichiers sous-jacent. Cette cale présente une interface aux six couches supérieures de SQLite qui donne l'impression que de très gros fichiers sont utilisés, alors qu'en réalité, chaque fichier aussi gros est divisé en de nombreux fichiers plus petits sur le système sous-jacent. Cette shim a été utilisée, par exemple, pour permettre aux bases de données de croître au-delà de 2 gibytes sur des systèmes de fichiers FAT16.

  • test_onefile.c - Ce fichier implémente un VFS de démonstration nommé "fs" qui montre comment SQLite peut être utilisé sur un périphérique embarqué dépourvu de système de fichiers. Le contenu est écrit directement sur le support sous-jacent. Un VFS dérivé de ce code de démonstration pourrait être utilisé par un gadget avec une quantité limitée de mémoire flash pour que SQLite se comporte comme le système de fichiers pour la mémoire flash du dispositif.

  • test_journal.c - Ce fichier implémente un shim utilisé pendant les tests de SQLite qui vérifie que la base de données et le journal de retour en arrière sont écrits dans le bon ordre et sont "synchronisés" à des moments appropriés afin de garantir que la base de données peut se rétablir d'une perte de puissance ou d'un hard reset à tout moment. La cale vérifie plusieurs invariants sur le fonctionnement des bases de données et des journaux de retour en arrière et lève des exceptions si l'un de ces invariants est violé. Ces invariants, à leur tour, garantissent que la base de données est toujours récupérable. L'exécution d'une grande suite de cas de test en utilisant cette shim fournit une assurance supplémentaire que les bases de données SQLite ne seront pas endommagées par des pannes de courant inattendues ou des réinitialisations de périphériques.

  • test_vfs.c - Ce fichier implémente une shim qui peut être utilisée pour simuler des fautes de système de fichiers. Ce shim est utilisé pendant les tests pour vérifier que SQLite répond sainement aux dysfonctionnements matériels ou à d'autres conditions d'erreur telles que l'épuisement de l'espace du système de fichiers qui sont difficiles à tester sur un système réel.

Il existe d'autres implémentations VFS à la fois dans la bibliothèque de code source de SQLite de base et dans les extensions disponibles. La liste ci-dessus n'est pas censée être exhaustive mais simplement représentative des types de fonctionnalités qui peuvent être réalisées en utilisant l'interface VFS.

Un nouveau VFS est implémenté en sous-classant trois objets :

  • sqlite3_vfs
  • sqlite3_io_methods
  • sqlite3_file

Un objet sqlite3_vfs définit le nom du VFS et les méthodes principales qui mettent en œuvre l'interface avec le système d'exploitation, comme la vérification de l'existence de fichiers, la suppression de fichiers, la création de fichiers et l'ouverture et pour la lecture et/ou l'écriture, la conversion des noms de fichiers dans leur forme canonique. L'objet sqlite3_vfs contient également des méthodes pour obtenir le caractère aléatoire du système d'exploitation, pour suspendre un processus (sleeping) et pour trouver la date et l'heure actuelles.

L'objet sqlite3_file représente un fichier ouvert. La méthode xOpen de sqlite3_vfs construit un objet sqlite3_file lorsque le fichier est ouvert. L'objet sqlite3_file garde la trace de l'état du fichier pendant son ouverture.

L'objet sqlite3_io_methods contient les méthodes utilisées pour interagir avec un fichier ouvert. Chaque sqlite3_file contient un pointeur vers un objet sqlite3_io_methods qui est approprié pour le fichier qu'il représente. L'objet sqlite3_io_methods contient des méthodes pour faire des choses telles que lire et écrire à partir du fichier, pour tronquer le fichier, pour flush toute modification au stockage persistant, pour trouver la taille du fichier, pour verrouiller et déverrouiller le fichier, et pour fermer le fichier et détruire l'objet sqlite3_file.

L'écriture du code pour un nouveau VFS implique la construction d'une sous-classe pour l'objet sqlite3_vfs et ensuite l'enregistrement de cet objet VFS en utilisant un appel à sqlite3_vfs_register(). L'implémentation de VFS fournit également des sous-classes pour sqlite3_file et sqlite3_io_methods mais ces objets ne sont pas enregistrés directement avec SQLite. Au lieu de cela, l'objet sqlite3_file est retourné par la méthode xOpen de sqlite3_vfs et l'objet sqlite3_file pointe vers une instance de l'objet sqlite3_io_methods.