1. L'extension RBU2. Les mises à jour RBU2.1. Limites de la mise à jour RBU2.2. Préparation d'un fichier de mise à jour RBU2.2.1. Le schéma de la base de données RBU2.2.2. Contenu de la base de données RBU2.2.3. Utiliser RBU avec des tables FTS3/42.2.4. Générer automatiquement des mises à jour RBU avec sqldiff2.3. Programmation C/C++ des mises à jour RBU3. RBU Vacuum3.1. Limites de RBU Vacuum3.2. Programmation C/C++ de RBU Vacuum

L'extension RBU est un add-on pour SQLite conçu pour l'utilisation de gros fichiers de base de données SQLite sur des appareils de faible puissance à la périphérie d'un réseau. RBU peut être utilisé pour deux tâches distinctes :

  • Les opérations de mise à jour de RBU. Une mise à jour RBU est une mise à jour en masse d'un fichier de base de données qui peut inclure de nombreuses opérations d'insertion, de mise à jour et de suppression sur une ou plusieurs tables.
  • Opérations d'aspiration RBU. Un RBU Vacuum optimise et reconstruit un fichier de base de données entier, avec des résultats similaires à la commande VACUUM native de SQLite.

L'acronyme RBU signifie " mise à jour en masse résumable ".

Les deux fonctions RBU peuvent être accomplies en utilisant les commandes SQL intégrées de SQLite - la mise à jour RBU via une série de commandes INSERT, DELETE et UPDATE au sein d'une seule transaction, et le vide RBU par une seule commande VACUUM. Le module RBU offre les avantages suivants par rapport à ces approches plus simples :

  1. RBU peut être plus efficace

    La manière la plus efficace d'appliquer des modifications à un B-Tree (la structure de données que SQLite utilise pour stocker chaque table et index sur le disque) est d'effectuer les modifications dans l'ordre des clés. Mais si une table SQL possède un ou plusieurs index, l'ordre des clés pour chaque index peut être différent de celui de la table principale et des autres index auxiliaires. Par conséquent, lors de l'exécution d'une série d'instructions INSERT, UPDATE et DELETE, il n'est généralement pas possible d'ordonner les opérations de manière à ce que tous les b-trees soient mis à jour dans l'ordre des clés. Le processus de mise à jour RBU contourne ce problème en appliquant toutes les modifications à la table principale en une seule passe, puis en appliquant les modifications à chaque index en passes séparées, garantissant que chaque B-Tree est mis à jour de manière optimale. Pour un grand fichier de base de données (un qui ne tient pas dans le cache disque du système d'exploitation), cette procédure peut entraîner des mises à jour plus rapides de deux ordres de grandeur.

    Une opération Vacuum RBU nécessite moins d'espace disque temporaire et écrit moins de données sur le disque qu'un VACUUM SQLite. Un VACUUM SQLite nécessite environ deux fois la taille du fichier de base de données final en espace disque temporaire pour s'exécuter. La quantité totale de données écrites est environ trois fois la taille du fichier de base de données final. En revanche, un VACUUM RBU nécessite à peu près la taille du fichier de base de données final dans l'espace disque temporaire et écrit un total de deux fois cette taille sur le disque.

    D'autre part, un RBU Vacuum utilise plus de CPU qu'un VACUUM SQLite ordinaire - dans un test, jusqu'à cinq fois plus. Pour cette raison, un RBU Vacuum est souvent significativement plus lent qu'un SQLite VACUUM dans les mêmes conditions.

  2. RBU fonctionne en arrière-plan

    Une opération RBU en cours (que ce soit une mise à jour ou un vide) n'interfère pas avec l'accès en lecture au fichier de base de données.

  3. La RBU s'exécute de manière incrémentielle

    Les opérations RBU peuvent être suspendues puis reprises plus tard, peut-être avec des coupures de courant intermédiaires et/ou des réinitialisations du système. Pour une mise à jour RBU, le contenu original de la base de données reste visible pour tous les lecteurs de la base de données jusqu'à ce que la totalité de la mise à jour ait été appliquée - même si la mise à jour est suspendue puis reprise plus tard.

L'extension RBU n'est pas activée par défaut. Pour l'activer, compilez l'amalgame avec l'option de compilation SQLITE_ENABLE_RBU.

2.1. Limitations de la mise à jour RBU

Les limitations suivantes s'appliquent aux mises à jour RBU :

  • Les modifications doivent consister uniquement en des opérations INSERT, UPDATE et DELETE. Les opérations CREATE et DROP ne sont pas prises en charge.

  • Les instructions INSERT ne peuvent pas utiliser les valeurs par défaut.

  • Les instructions UPDATE et DELETE doivent identifier les lignes cibles par rowid ou par des valeurs PRIMARY KEY non nulles.

  • Les instructions UPDATE ne peuvent pas modifier les valeurs PRIMARY KEY ou rowid.

  • Les mises à jour RBU ne peuvent être appliquées à aucune table contenant une colonne nommée "rbu_control".

  • La mise à jour RBU ne déclenchera aucun déclencheur.

  • La mise à jour RBU ne détectera ni n'empêchera les violations de clés étrangères ou de contraintes CHECK.

  • Toutes les mises à jour RBU utilisent le mécanisme de traitement des contraintes "OR ROLLBACK".

  • La base de données cible peut ne pas être en mode WAL.

  • La base de données cible peut ne pas contenir d'index sur les expressions. Les index sur les expressions sont pris en charge à partir de SQLite 3.30.0 (2019-10-04).
  • Aucune autre écriture ne peut se produire sur la base de données cible pendant l'application de la mise à jour RBU. Un verrou de lecture est maintenu sur la base de données cible pour empêcher cela.

2.2. Préparation d'un fichier de mise à jour RBU

Toutes les modifications à appliquer par RBU sont stockées dans une base de données SQLite séparée appelée "base de données RBU". La base de données qui doit être modifiée est appelée la "base de données cible".

Pour chaque table de la base de données cible qui sera modifiée par la mise à jour, une table correspondante est créée dans la base de données RBU. Le schéma de table de la base de données RBU n'est pas le même que celui de la base de données cible, mais il en est dérivé comme décrit ci-dessous.

La table de la base de données RBU contient une seule ligne pour chaque ligne de la base de données cible insérée, mise à jour ou supprimée par la mise à jour. Le peuplement des tables de la base de données RBU est décrit dans la section suivante.

2.2.1. Le schéma de la base de données RBU

Pour chaque table de la base de données cible, la base de données RBU doit contenir une table nommée "data<....entier _ nom de la table cible" où nom-table cible est le nom de la table dans la base de données cible et nombre entier est toute séquence de zéro ou plusieurs caractères numériques (0-9). Les tables de la base de données RBU sont traitées dans l'ordre de leur nom (du plus petit au plus grand selon la séquence de collationnement BINARY), de sorte que l'ordre dans lequel les tables cibles sont mises à jour est influencé par la sélection de la valeur .entier de la partie du nom de la table data_%. Bien que cela puisse être utile lors de l'utilisation de RBU pour mettre à jour certains types de tables virtuelles, il n'y a normalement aucune raison d'utiliser autre chose qu'une chaîne vide à la place de entier.nombre entier.

La table data_% doit avoir toutes les mêmes colonnes que la table cible, plus une colonne supplémentaire nommée "rbu_control". La table data_% ne doit pas avoir de contraintes PRIMARY KEY ou UNIQUE, mais chaque colonne doit avoir le même type que la colonne correspondante dans la base de données cible. La colonne rbu_control ne doit pas avoir de type du tout. Par exemple, si la base de données cible contient :

CREATETABLE t1(a INTEGERPRIMARYKEY, b TEXT, c UNIQUE);

Alors la base de données RBU doit contenir :

CREATETABLE data_t1(a INTEGER, b TEXT, c, rbu_control);

L'ordre des colonnes dans la table data_% n'a pas d'importance.

Si la table de la base de données cible est une table virtuelle ou une table qui n'a pas de déclaration PRIMARY KEY, la table data_% doit également contenir une colonne nommée "rbu_rowid". La colonne rbu_rowid est mise en correspondance avec le ROWID de la table. Par exemple, si la base de données cible contient l'un des éléments suivants :

CREATE VIRTUAL TABLE x1 USING fts3(a, b);CREATETABLE x1(a, b);

alors la base de données RBU doit contenir :

CREATETABLE data_x1(a, b, rbu_rowid, rbu_control);

Les tables virtuelles pour lesquelles la colonne "rowid" ne fonctionne pas comme une valeur de clé primaire ne peuvent pas être mises à jour en utilisant RBU.

Toutes les colonnes non cachées (c'est-à-dire toutes les colonnes mises en correspondance par "SELECT *") de la table cible doivent être présentes dans la table d'entrée. Pour les tables virtuelles, les colonnes cachées sont facultatives - elles sont mises à jour par RBU si elles sont présentes dans la table d'entrée, ou pas sinon. Par exemple, pour écrire dans une table fts4 avec une colonne cachée languageid telle que :

CREATE VIRTUAL TABLE ft1 USING fts4(a, b, languageid='langid');

L'un ou l'autre des schémas de table d'entrée suivants peut être utilisé :

CREATETABLE data_ft1(a, b, langid, rbu_rowid, rbu_control);CREATETABLE data_ft1(a, b, rbu_rowid, rbu_control);

2.2.2. Contenu de la base de données RBU

Pour chaque ligne à INSERER dans la base de données cible dans le cadre de la mise à jour RBU, la table data_% correspondante doit contenir un seul enregistrement dont la colonne "rbu_control" doit contenir la valeur entière 0. Les autres colonnes doivent être définies sur les valeurs qui constituent le nouvel enregistrement à insérer.

La colonne "rbu_control" peut également être définie à la valeur entière 2 pour un INSERT. Dans ce cas, le nouvel enregistrement remplace silencieusement tout enregistrement existant qui a les mêmes valeurs de clé primaire. Ceci est équivalent à un DELETE suivi d'un INSERT avec les mêmes valeurs de clé primaire. Ce n'est pas la même chose qu'une commande SQL REPLACE, car dans ce cas, la nouvelle rangée peut remplacer toute rangée en conflit (c'est-à-dire celles qui entrent en conflit en raison de contraintes UNIQUE ou d'index), et pas seulement celles qui ont des clés primaires en conflit.

Si la table de la base de données cible possède une CLÉ PRIMAIRE INTEGRALE, il n'est pas possible d'insérer une valeur NULL dans la colonne IPK. Toute tentative en ce sens entraîne une erreur SQLITE_MISMATCH.

Pour chaque rangée à SUPPRIMER de la base de données cible dans le cadre de la mise à jour RBU, la table data_% correspondante doit contenir un seul enregistrement dont la colonne "rbu_control" est définie pour contenir la valeur entière 1. Les valeurs réelles de la clé primaire de la ligne à supprimer doivent être stockées dans les colonnes correspondantes de la table data_%. Les valeurs stockées dans les autres colonnes ne sont pas utilisées.

Pour chaque ligne à UPDATE de la base de données cible dans le cadre de la mise à jour RBU, la table data_% correspondante doit contenir un seul enregistrement dont la colonne "rbu_control" doit contenir une valeur de type texte. Les valeurs réelles de la clé primaire identifiant la ligne à mettre à jour doivent être stockées dans les colonnes correspondantes de la ligne de la table data_%, tout comme les nouvelles valeurs de toutes les colonnes mises à jour. La valeur texte de la colonne "rbu_control" doit contenir le même nombre de caractères que celui des colonnes de la table de la base de données cible, et doit être entièrement constituée de caractères 'x' et '.' (ou dans certains cas particuliers 'd' - voir ci-dessous). Pour chaque colonne qui est mise à jour, le caractère correspondant prend la valeur 'x'. Pour celles qui restent telles quelles, le caractère correspondant de la valeur rbu_control doit être défini comme '.'. Par exemple, étant donné les tableaux ci-dessus, l'instruction de mise à jour :

UPDATE t1 SET c ='usa'WHERE a =4;

est représentée par la ligne data_t1 créée par :

INSERTINTO data_t1(a, b, c, rbu_control)VALUES(4,NULL,'usa','..x');

Si RBU est utilisé pour mettre à jour une grande valeur BLOB dans une base de données cible, il peut être plus efficace de stocker un patch ou un delta qui peut être utilisé pour modifier le BLOB existant plutôt qu'une valeur entièrement nouvelle dans la base de données RBU. RBU permet de spécifier les deltas de deux manières :

  • Dans le format "fossil delta" - le format utilisé pour les deltas de blob par le système de gestion des données. système de gestion du code source Fossil. ou
  • Dans un format personnalisé défini par l'application RBU.

Le format fossil delta ne peut être utilisé que pour mettre à jour des valeurs BLOB. Au lieu de stocker le nouveau BLOB dans la table data_%, le delta fossile est stocké à la place. Et au lieu de spécifier un 'x' dans la chaîne rbu_control pour la colonne à mettre à jour, un caractère 'f' est stocké. Lors du traitement d'une mise à jour 'f', RBU charge les données BLOB originales depuis le disque, leur applique le delta fossile et enregistre les résultats dans le fichier de la base de données. Les bases de données RBU générées par sqldiff --rbu font usage des deltas fossiles chaque fois que cela permet d'économiser de l'espace dans la base de données RBU.

Pour utiliser un format delta personnalisé, l'application RBU doit enregistrer une fonction SQL définie par l'utilisateur nommée " rbu_delta " avant de commencer à traiter la mise à jour. rbu_delta() sera invoquée avec deux arguments - la valeur originale stockée dans la colonne de la table cible et la valeur delta fournie dans le cadre de la mise à jour RBU. Elle doit renvoyer le résultat de l'application du delta à la valeur originale. Pour utiliser la fonction delta personnalisée, le caractère de la valeur rbu_control correspondant à la colonne cible à mettre à jour doit être défini sur 'd' au lieu de 'x'. Ensuite, au lieu de mettre à jour la table cible avec la valeur stockée dans la colonne data_% correspondante, RBU invoque la fonction SQL définie par l'utilisateur "rbu_delta()" et le stockage dans la colonne de la table cible.

Par exemple, cette ligne :

INSERTINTO data_t1(a, b, c, rbu_control)VALUES(4,NULL,'usa','..d');

entraîne la mise à jour par RBU de la table de base de données cible d'une manière similaire à :

UPDATE t1 SET c = rbu_delta(c,'usa')WHERE a =4;

Si la table de base de données cible est une table virtuelle ou une table sans PRIMARY KEY, la valeur rbu_control ne doit pas inclure un caractère correspondant à la valeur rbu_rowid. Par exemple, ceci :

INSERTINTO data_ft1(a, b, rbu_rowid, rbu_control)VALUES(NULL,'usa',12,'.x');

provoque un résultat similaire à :

UPDATE ft1 SET b ='usa'WHERE rowid =12;

Les tables data_% elles-mêmes ne doivent pas avoir de déclaration de PRIMARY KEY. Cependant, la RBU est plus efficace si la lecture des lignes dans chaque table data_% dans l'ordre "rowid" est à peu près la même que la lecture triée par la PRIMARY KEY de la table de base de données cible correspondante. En d'autres termes, les lignes doivent être triées en utilisant les champs PRIMARY KEY de la table de destination avant d'être insérées dans les tables data_%.

2.2.3. Utilisation de la RBU avec des tables FTS3/4

Habituellement, une table FTS3 ou FTS4 est un exemple de table virtuelle avec un rowid qui fonctionne comme une PRIMARY KEY. Ainsi, pour les tables FTS4 suivantes :

CREATE VIRTUAL TABLE ft1 USING fts4(addr,text);CREATE VIRTUAL TABLE ft2 USING fts4;-- implicit "content" column

Les tables data_% peuvent être créées comme suit :

CREATETABLE data_ft1 USING fts4(addr,text, rbu_rowid, rbu_control);CREATETABLE data_ft2 USING fts4(content, rbu_rowid, rbu_control);

Et alimentées comme si la table cible était une table SQLite ordinaire sans colonnes PRIMARY KEY explicites.

Les tables FTS4 sans contenu sont traitées de manière similaire, sauf que toute tentative de mise à jour ou de suppression de lignes provoquera une erreur lors de l'application de la mise à jour.

Les tables FTS4 de contenu externe peuvent également être mises à jour à l'aide de RBU. Dans ce cas, l'utilisateur doit configurer la base de données RBU pour que le même ensemble d'opérations UPDATE, DELETE et INSERT soit appliqué à l'index FTS4 qu'à la table de contenu sous-jacente. Comme pour toutes les mises à jour de tables FTS4 à contenu externe, l'utilisateur doit également s'assurer que les opérations UPDATE ou DELETE sont appliquées à l'index FTS4 avant d'être appliquées à la table de contenu sous-jacente (voir la documentation FTS4 pour une explication détaillée). Dans RBU, cela se fait en s'assurant que le nom de la table data_% utilisée pour écrire dans la table FTS4 est trié avant le nom de la table data_% utilisée pour mettre à jour la table de contenu sous-jacente en utilisant la séquence de collation BINARY. Afin d'éviter la duplication des données dans la base de données RBU, une vue SQL peut être utilisée à la place de l'une des tables data_%. Par exemple, pour le schéma de la base de données cible :

CREATETABLE ccc(addr,text);CREATE VIRTUAL TABLE ccc_fts USING fts4(addr,text, content=ccc);

Le schéma de base de données RBU suivant peut être utilisé :

CREATETABLE data_ccc(addr,text, rbu_rowid, rbu_control);CREATEVIEW data0_ccc_fts ASSELECT*FROM data_ccc;

La table data_ccc peut alors être alimentée normalement avec les mises à jour destinées à la table ccc de la base de données cible. Les mêmes mises à jour seront lues par RBU à partir de la vue data0_ccc_fts et appliquées à la table FTS ccc_fts. Comme "data0_ccc_fts" est plus petite que "data_ccc", la table FTS sera mise à jour en premier, si nécessaire.

Les cas dans lesquels la table de contenu sous-jacente possède une colonne PRIMARY KEY INTEGER explicite sont légèrement plus difficiles, car les valeurs de texte stockées dans la colonne rbu_control sont légèrement différentes pour l'index FTS et sa table de contenu sous-jacente. Pour la table de contenu sous-jacente, un caractère doit être inclus dans toutes les valeurs de texte rbu_control pour l'IPK explicite, mais pour la table FTS elle-même, qui a un rowid implicite, il ne doit pas l'être. Cela est gênant, mais peut être résolu en utilisant une vue plus compliquée, comme suit :

-- Target database schemaCREATETABLE ddd(i INTEGERPRIMARYKEY, k TEXT);CREATE VIRTUAL TABLE ddd_fts USING fts4(k, content=ddd);-- RBU database schemaCREATETABLE data_ccc(i, k, rbu_control);CREATEVIEW data0_ccc_fts ASSELECT i AS rbu_rowid, k,CASEWHEN rbu_control IN(0,1)THEN rbu_control ELSE substr(rbu_control,2)ENDFROM data_ccc;

La fonction substr() de la vue SQL ci-dessus renvoie le texte de l'argument rbu_control avec le premier caractère (celui correspondant à la colonne "i", qui n'est pas requis par la table FTS) supprimé.

2.2.4. Génération automatique des mises à jour de la RBU avec sqldiff.

A partir de SQLite version 3.9.0 (2015-10-14), l'utilitaire sqldiff est capable de générer des bases de données RBU représentant la différence entre deux bases de données aux schémas identiques. Par exemple, la commande suivante :

sqldiff --rbu t1.db t2.db

Sort un script SQL pour créer une base de données RBU qui, si elle est utilisée pour mettre à jour la base de données t1.db, la corrige pour que son contenu soit identique à celui de la base de données t2.db.

Par défaut, sqldiff tente de traiter toutes les tables non virtuelles au sein des deux bases de données qui lui sont fournies. Si une table apparaît dans une base de données mais pas dans l'autre, ou si une table a un schéma légèrement différent dans une base de données, c'est une erreur. L'option "--table" peut être utile si cela pose un problème

Les tables virtuelles sont ignorées par défaut par sqldiff. Cependant, il est possible de créer explicitement une table RBU data_% pour une table virtuelle qui présente un rowid qui fonctionne comme une clé primaire en utilisant une commande telle que :

sqldiff --rbu --table  t1.db t2.db

Malheureusement, même si les tables virtuelles sont ignorées par défaut, toutes les tables de base de données sous-jacentes qu'elles créent afin de stocker des données dans la base de données ne le sont pas, et sqldiff inclura ces dernières dans toute base de données RBU. Pour cette raison, les utilisateurs qui tentent d'utiliser sqldiff pour créer des mises à jour RBU à appliquer aux bases de données cibles avec une ou plusieurs tables virtuelles devront probablement exécuter sqldiff en utilisant l'option --table séparément pour chaque table à mettre à jour dans la base de données cible.

2.3. Programmation C/C++ des mises à jour RBU

L'interface d'extension RBU permet à une application d'appliquer une mise à jour RBU stockée dans une base de données RBU à une base de données cible existante. La procédure est la suivante :

  1. Ouvrez un handle RBU en utilisant la fonction sqlite3rbu_open(T,A,S).

    L'argument T est le nom du fichier de la base de données cible. L'argument A est le nom du fichier de base de données RBU. L'argument S est le nom d'une "base de données d'état" utilisée pour stocker les informations d'état nécessaires à la reprise de la mise à jour après une interruption. L'argument S peut être NULL, auquel cas les informations d'état sont stockées dans la base de données RBU dans diverses tables dont les noms commencent tous par "rbu_".

    La fonction sqlite3rbu_open(T,A,S) renvoie un pointeur vers un objet "sqlite3rbu", qui est ensuite passé dans les interfaces suivantes.

  2. Enregistrez tous les modules de table virtuelle requis avec le handle de base de données renvoyé par sqlite3rbu_db(X) (où l'argument X est le pointeur sqlite3rbu renvoyé par sqlite3rbu_open()). Si nécessaire, enregistrez également la fonction SQL rbu_delta() à l'aide de sqlite3_create_function_v2().

  3. Invoquez la fonction sqlite3rbu_step(X) une ou plusieurs fois sur le pointeur d'objet sqlite3rbu X. Chaque appel à sqlite3rbu_step() effectue une seule opération b-tree, de sorte que des milliers d'appels peuvent être nécessaires pour appliquer une mise à jour complète. L'interface sqlite3rbu_step() renvoie SQLITE_DONE lorsque la mise à jour a été complètement appliquée.

  4. Appelez sqlite3rbu_close(X) pour détruire le pointeur d'objet sqlite3rbu. Si sqlite3rbu_step(X) a été appelé suffisamment de fois pour appliquer complètement la mise à jour à la base de données cible, alors la base de données RBU est marquée comme étant complètement appliquée. Sinon, l'état de l'application de la mise à jour RBU est enregistré dans la base de données d'état (ou dans la base de données RBU si le nom du fichier de la base de données d'état dans sqlite3rbu_open() est NULL) pour une reprise ultérieure de la mise à jour.

Si une mise à jour n'est que partiellement appliquée à la base de données cible au moment où sqlite3rbu_close() est appelé, les informations d'état sont enregistrées dans la base de données d'état si elle existe, ou sinon dans la base de données RBU. Cela permet aux processus suivants de reprendre automatiquement la mise à jour RBU là où elle s'est arrêtée. Si les informations d'état sont enregistrées dans la base de données RBU, elles peuvent être supprimées en abandonnant toutes les tables dont le nom commence par "rbu_".

Pour plus de détails, reportez-vous aux commentaires de la section le fichier d'en-tête sqlite3rbu.h..

3.1. Limitations du vide RBU

Par rapport à la commande VACUUM intégrée de SQLite, RBU Vacuum présente les limitations suivantes :

  • Elle ne peut pas être utilisée sur une base de données qui contient des index sur des expressions.

  • La base de données aspirée ne peut pas être en mode WAL.

3.2. Programmation RBU Vacuum C/C++

Cette section fournit un aperçu et un exemple de code démontrant l'intégration de RBU Vacuum dans un programme d'application. Pour des détails complets, reportez-vous aux commentaires de la section fichier d'en-tête sqlite3rbu.h.

Les applications RBU Vacuum mettent toutes en œuvre une variation de la procédure suivante :

  1. Un handle RBU est créé en appelant sqlite3rbu_vacuum(T, S).

    L'argument T est le nom du fichier de la base de données à aspirer. L'argument S est le nom d'une base de données dans laquelle le module RBU sauvegardera son état si l'opération de vide est suspendue.

    Si la base de données d'état S n'existe pas lorsque sqlite3rbu_vacuum() est invoqué, elle est automatiquement créée et remplie avec la table unique utilisée pour stocker l'état d'un vide RBU - "rbu_state". Si un vide RBU en cours est suspendu, cette table est remplie de données d'état. La prochaine fois que sqlite3rbu_vacuum() est appelé avec le même paramètre S, il détecte ces données et tente de reprendre l'opération de vide suspendue. Lorsqu'une opération de vide RBU est terminée ou rencontre une erreur, RBU supprime automatiquement le contenu de la table rbu_state. Dans ce cas, l'appel suivant à sqlite3rbu_vacuum() démarre une toute nouvelle opération de vide à partir de zéro.

    C'est une bonne idée d'établir une convention pour déterminer le nom de la base de données d'état de vide RBU en fonction du nom de la base de données cible. L'exemple de code ci-dessous utilise "-vacuum", où est le nom de la base de données mise sous vide.

  2. Toute séquence de collation personnalisée utilisée par les index dans la base de données en cours d'aspiration est enregistrée avec les deux handles de base de données retournés par la fonction sqlite3rbu_db().

  3. La fonction sqlite3rbu_step() est appelée sur le handle RBU jusqu'à ce que l'aspiration RBU soit terminée, qu'une erreur se produise ou que l'application souhaite suspendre l'aspiration RBU.

    Chaque appel à sqlite3rbu_step() effectue une petite quantité de travail pour terminer l'opération de vide. Selon la taille de la base de données, un seul vide peut nécessiter des milliers d'appels à sqlite3rbu_step(). sqlite3rbu_step() renvoie SQLITE_DONE si l'opération de vide est terminée, SQLITE_OK si l'opération de vide n'est pas terminée mais qu'aucune erreur ne s'est produite, et un code d'erreur SQLite si une erreur est rencontrée. Si une erreur se produit, tous les appels ultérieurs à sqlite3rbu_step() renvoient immédiatement le même code d'erreur.

  4. Enfin, sqlite3rbu_close() est appelé pour fermer le handle RBU. Si l'application a cessé d'appeler sqlite3rbu_step() avant que le vide ne se termine ou qu'une erreur ne se produise, l'état du vide est sauvegardé dans la base de données des états afin qu'il puisse être repris ultérieurement.

    Comme sqlite3rbu_step(), si l'opération de vide est terminée, sqlite3rbu_close() renvoie SQLITE_DONE. Si l'opération de vide n'est pas terminée mais qu'aucune erreur ne s'est produite, SQLITE_OK est retourné. Ou, si une erreur s'est produite, un code d'erreur SQLite est renvoyé. Si une erreur s'est produite dans le cadre d'un appel antérieur à sqlite3rbu_step(), sqlite3rbu_close() renvoie le même code d'erreur.

L'exemple de code suivant illustre les techniques décrites ci-dessus.

/*
** Either start a new RBU vacuum or resume a suspended RBU vacuum on 
** database zTarget. Return when either an error occurs, the RBU 
** vacuum is finished or when the application signals an interrupt
** (code not shown).
**
** If the RBU vacuum is completed successfully, return SQLITE_DONE.
** If an error occurs, return SQLite error code. Or, if the application
** signals an interrupt, suspend the RBU vacuum operation so that it
** may be resumed by a subsequent call to this function and return
** SQLITE_OK.
**
** This function uses the database named "-vacuum" for
** the state database, where  is the name of the database 
** being vacuumed.
*/int do_rbu_vacuum(const char*zTarget){
  int rc;char*zState;/* Name of state database */
  sqlite3rbu *pRbu;/* RBU vacuum handle */

  zState = sqlite3_mprintf("%s-vacuum", zTarget);if( zState==0)return SQLITE_NOMEM;
  pRbu = sqlite3rbu_vacuum(zTarget, zState);
  sqlite3_free(zState);if( pRbu ){
    sqlite3 *dbTarget = sqlite3rbu_db(pRbu,0);
    sqlite3 *dbState = sqlite3rbu_db(pRbu,1);/* Any custom collation sequences used by the target database must
    ** be registered with both database handles here.  */while( sqlite3rbu_step(pRbu)==SQLITE_OK ){
      if(<application has signaled interrupt>)break;
    }
  }
  rc = sqlite3rbu_close(pRbu);return rc;
}