Sqlite.jsm

Le module de code JavaScript Sqlite.jsm offre une interface de stockage/SQLite. Sqlite.jsm fonctionne avec des interfaces XPCOM de stockage de bas niveau:

  • la gestion de la déclaration est automatique. Sqlite.jsm va créer, gérer et détruire les instances d'instructions pour vous. Vous ne devez pas vous soucier de la mise en cache des instances des états créés, les détruire lorsque vous avez terminé, etc. Cela se traduit par moins de lignes de code pour communiquer avec SQLite.
  • Toutes les opérations sont asynchrones. Utilisation des API de stockage synchrones est déconseillée, car ils bloquent le thread principal. Toutes les fonctionnalités de Sqlite.jsm sont asynchrone.
  • La gestion de la mémoire est plus facile. Sqlite.jsm gère les déclarations pour vous, il peut effectuer des actions intelligentes comme purger toutes les déclarations mises en cache qui ne sont pas utilisés, ce qui libère la mémoire dans le processus. Il y a même une API shrinkMemory qui permettra de minimiser automatiquement l'utilisation de la mémoire de la connexion.
  • Des opérations sont simples. Sqlite.jsm utilise une API de transaction construit avec  task.jsm qui permet aux transactions d'être écrites en tant que fonctions de procédure JavaScript (par opposition à une série d'opérations motrices de rappel). Si la fonction est lancée, la transaction est automatiquement annulée. Cela rend le code facile à lire et à écrire.
  • Sqlite.jsm est un module JavaScript pur. Les complexités de XPCOM sont la plupart du temps cachés. Les programmeurs JavaScript doivent se sentir à l'aise en l'utilisant.

Note : Le module de code de Javascript Sqlite.jsm peut seulement être utilisé du chrome.

Avant de pouvoir utiliser ce module, vous devez l'importer dans votre champ d'application:

let { Cu } = require('chrome')
Cu.import("resource://gre/modules/Sqlite.jsm")

Obtention d'une connexion

Sqlite.jsm exporte le symbole Sqlite. Ce symbole est un objet avec une seule fonction: openConnection. Cette fonction prend un objet dictionnaire définissant les options de connexion:

path
(Obligatoire) Le fichier de base de données à ouvrir. Cela peut être un chemin absolu ou relatif. Si un chemin relatif est donné, il est interprété comme relatif au répertoire du profil actuel. Si le chemin n'existe pas, une nouvelle base de données SQLite sera créé. La nom se termine généralement par .sqlite.
sharedMemoryCache
(En option) booléenne indiquant si plusieurs connexions à la base de données partagent la même mémoire cache. Toutefois, le partage nécessite également des connexions pour obtenir un verrou, rendant éventuellement l'accès de base de données plus lente. Par défaut, true.
shrinkMemoryOnConnectionIdleMS
(En option) Si défini, le de connexion va tenter de minimiser son utilisation de la mémoire après un grand nombre millisecondes de connexion inactive. La connexion est inactive lorsque aucun états n'est exécuter. Noter que ceci n'est pas une minuterie qui pourrait se déclencher pendant que l'Application est active.

openConnection(options) retourne une promise d'une instance de connexion ouverte ou est rejetée si une erreur survient lors de l'ouverture de la base de données.

Voici un exemple:

let { Cu } = require('chrome')
Cu.import("resource://gre/modules/Sqlite.jsm");

Sqlite.openConnection({ path: "myDatabase.sqlite", sharedMemoryCache: false }).then(
  function onConnection(connection) {
    // connection is the opened SQLite connection (see below for API).
  },
  function onError(error) {
    // The connection could not be opened. error is an Error describing what went wrong.
  }
);

Utilisation des connexions ouvertes

Les connexions ouvertes sont ce que vous interfacer avec Sqlite.jsm. Les sections suivantes détaillent l'API d'une instance de connexion ouverte.

Gestion des connexions

Ces API sont utilisées pour gérer et vérifier l'état de la connexion.

close()

Ferme la connexion de base de données. Doit être appelé pour chaque connexion ouverte.

Cette fonction retourne une promise qui sera résolu lorsque la base de données sera fermée.

Si une transaction est en cours d'execution au moment où cette fonction est appelée, la transaction sera annulée.

Si des déclarations sont en cours au moment où cette fonction est appelée, elles seront annulées.

Les utilisateurs ne doivent pas tenter d'utiliser la connexion après avoir appelé cette méthode que la connexion sera inutilisable.

clone (readOnly)

Cette fonction retourne un clone de la connexion-promise actuelle.

Ces fonctions reçoivent l' argument:

readOnly
(En option) Si true le clone sera en lecture seule, false par défaut. Si la connexion d'origine est déjà en lecture seule, le clone le sera, indépendamment de cette option. Si la connexion d'origine utilise le cache partagé, ce paramètre sera ignoré et le clone sera aussi privilégié que la connexion d'origine.

transactionInProgress

Cette propriété booléenne indique si une opération est en cours. Cela est rarement nécessaire par les appelants externes.

shrinkMemory()

Cette fonction peut être appelée pour diminuer l'utilisation de la mémoire de la connexion. Ceci est un wrapper utilisé dans le PRAGMA shrink_memory, qui impose à SQLite la réduiction d'utilisation de la mémoire (par les caches de compensation, etc.).

Elle peut rendre votre base de données plus lent. Par conséquent, il faut être prudent avant d'appeler cette fonction.

Cela renvoie une promise qui est résolu lorsque l'opération est terminée.

discardCachedStatements()

Cette fonction est utilisée pour éliminer les instances d'instructions mises en cache, ce qui libère la mémoire dans le processus. Les déclarations de cache actifs ne seront pas effacées. Peut être appeler à tout moment.

Cela renvoie le nombre de déclarations mises en cache qui ont été rejetés.

Table et gestion du schéma

Ces API traitent de la gestion des tables et le schéma de base de données.

getSchemaVersion()

La version définie par l'utilisateur associé au schéma de la base de données actuelle. Retourne 0 si aucune version de schéma a été défini.

setSchemaVersion(value)

Definie la valeur value de la nouvelle version associée au schéma de la base de données actuelle. Ceci est un wrapper autour de PRAGMA user_version.

tableExists(name)

Cette fonction détermine si une table existe dans la base de données actuelle. Elle renvoie une promise qui est résolu avec une valeur booléenne indiquant si la table existe.

indexExists(name)

Cette fonction détermine si un index nommé existe dans la base de données actuelle. Elle renvoie une promesse qui est résolu avec une valeur booléenne indiquant si l'index existe.

Déclaration d'exécution

Ces API facilitent l'exécution des instructions de connexion.

executeCached(sql, params, onRow)

execute(sql, params, onRow)

Ces fonctions similaires sont utilisés pour exécuter une instruction SQL unique sur la connexion. Comme vous l'avez deviné par le nom, il y a 2 options: mises en cache et non mis en cache. En dehors de cela, ils se comportent de manière identique.

Ces fonctions reçoivent les arguments suivants:

sql
(Obligatoire) chaîne SQL à exécuter. Le point-virgule final n'est pas nécessaire.
params
(En option) Paramètres liés à cette déclaration. Cela peut être un tableau ou un objet. Voir les notes ci-dessous.
onRow
(En option) Fonction qui est appelée lorsqu'une ligne a été reçue.

La valeur de retour est une promise qui est résolu lorsque l'instruction a terminé l'exécution.

Quand une instruction est exécutée via executeCached(), l'objet instruction préparée est mis en cache à l'intérieur de la connexion ouverte. La prochaine fois que cette même instruction SQL est exécutée (sql argument est identique à celui passé avant), l'ancien objet de la déclaration est réutilisée. Cela permet d'économiser du temps associé à l'analyse de l'instruction SQL et la création d'un nouvel objet de déclaration. Inconvénient: l'objet de la déclaration en cache persiste dans la connexion ouverte, en prenant de la mémoire.

Quand une instruction est exécutée via execute(), l'objet de la déclaration sous-jacente est jeté au terme de l'exécution.

executeCached() est recommandé pour les déclarations qui seront exécutées plusieurs fois. execute() est recommandé pour les déclarations qui seront exécutées rarement ou une fois.

Noter que les utilisateurs ne doivent pas préparer les états manuellement avant l'exécution. Il suffit d'appeler executeCached() et la déclaration sera préparée pour vous automatiquement.

Les paramètres peuvent être liés à la déclaration faite par la définition de l'argument params . Cet argument peut être un tableau de paramètres ordonnées ou un objet de type dicionnaire. Si la déclaration ne contient pas de paramètres liés, cet argument peut être omis ou spécifié comme nulle.

Note : Les utilisateurs sont fortement encouragés à utiliser des paramètres liés au lieu de créer dynamiquement des instructions SQL pour des raisons de sécurité.

Note : Les utilisateurs sont encouragés à passer des objets type dictionnaire plutôt que des tableaux pour les paramètres liés car ils empêchent de s'emeler les pinceaux.
 
Lorsque onRow n'est pas défini, les résultats complets de l'opération sont tamponnés avant que l'appelant soit informé de la déclaration d'achèvement. Pour INSERT, UPDATE et DELETE, ce n'est pas pertinentes. Cependant, il peut y avoir des conséquences importante pour SELECT . Si votre déclaration SELECT retourne beaucoup de données, cette mise en mémoire tampon peut entraîner une utilisation excessive de la mémoire. Par conséquent, il est recommandé d'utiliser onRow avec SELECT.
 
Si StopIteration est emis lors de l'exécution d'un gestionnaire onRow, l'exécution de l'instruction est immédiatement annulée. Les lignes suivantes ne seront pas traitées. La promise est résolu immédiatement.
 
Si une exception StopIteration est levée par le gestionnaire onRow , l'exception est enregistré et le traitement des lignes suivantes se poursuit normalement. La promise est toujours résolue (pas rejetée).

La promise sera rejeté avec une Error , si la déclaration n'a pas terminé l'exécution complète. L'Error peut avoir une propriété errors. Si elle est définie, ce sera un tableau d'objets décrivant les erreurs. Chaque objet possède les propriétés result et message. result est un code d'erreur numérique et message est une chaîne décrivant le problème.

Si onRow est spécifié, la promise sera résolu avec un booléen indiquant si le gestionnaire onRow a été appelé. Sinon, la valeur réglée sera un tableau de mozIStorageRow .

executeTransaction(func, type)

Cette fonction est utilisée pour exécuter une transaction de base de données. Une transaction est une série de déclarations connexes traités comme une seule unité fonctionnelle. Si la transaction réussit, toutes les déclarations contenues dedans sont engagés comme une seule unité. Si la transaction échoue, la base de données revient à son état avant le début de la transaction.

Cette fonction reçoit les arguments suivants:

func
La fonction définissant le corps de la transaction.
type
Le type de transaction à effectuer. Ce doit être l'une des constantes de TRANSACTION_* sur l'instance de connexion ouverte. Les valeurs valides sont TRANSACTION_DEFERRED , TRANSACTION_IMMEDIATE , TRANSACTION_EXCLUSIVE . Consultez la documentation SQLite pour leur signification. La valeur par défaut est TRANSACTION_DEFERRED.

La fonction passée est une fonction de générateur compatible Task.jsm. Lorsqu'elle est appelée, la fonction reçoit comme argument l'instance de connexion actuelle. Cette fonction de générateur devrait produire des promises, probablement ceux qui sont renvoyés en appelant executeCached() et execute().

Si nous arrivons à la fin de la fonction de générateur sans erreur, la transaction est validée. Si une erreur se produit, la transaction est abandonnée.

La valeur retournée par cette fonction est une promise qui est résolu lorsque la transaction a été exécutée ou rejetée si la transaction a été annulée.

Exemples

Open, Execute, Close

Dans cet exemple, nous ouvrons une connexion, exécutons une instruction simple, puis fermons la connexion.

Sqlite.openConnection({path: "MyDB.sqlite"}).then(
  function onOpen(conn) {
    conn.execute("SELECT 1").then(
      function onStatementComplete(result) {
        conn.close().then(
          function onClose() {
            alert("We are done!");
          }
        )
      }
    )
  }
)

Ce n'est pas un excellent exemple parce qu'il ne comprend pas la gestion des erreurs et est un peu difficile à lire.

Voici la même chose mais avec Task.jsm:

Task.spawn(function* demoDatabase() {
  let conn = yield Sqlite.openConnection({path: "MyDB.sqlite"});

  try {
    let result = yield conn.execute("SELECT 1");
  } finally {
    yield conn.close();
  }
});

Bound Parameters

Voici quelques exemples montrant des paramètres liés. Supposons que nous ouvrons une connexion avec la variable conn.

let dataToInsert = [
  ["foo", "bar"],
  ["biz", "baz"],
  ["yo", "ho"],
];

Task.spawn(function* doInsert() {
  for (let data of dataToInsert) {
    yield conn.executeCached("INSERT INTO myTable VALUES (?, ?)", data);
  }
});

Et la même chose avec des paramètres nommés.

let dataToInsert = [
  {paramA: "foo", paramB: "bar"},
  {paramA: "biz", paramB: "baz"},
  {paramA: "yo", paramB: "ho"},
];

Task.spawn(function* doInsert() {
  for (let data of dataToInsert) {
    yield conn.executeCached("INSERT INTO myTable VALUES (:paramA, :paramB)", data);
  }
});

Transactions

Ces exemples montrent comment fonctionnent les transactions.

conn.executeTransaction(function* simpleTransaction() {
  yield conn.execute("INSERT INTO myTable VALUES (?, ?)", ["foo", "bar"]);
  yield conn.execute("INSERT INTO myTable VALUES (?, ?)", ["biz", "baz"]);
});

L'exemple ci-dessus se traduira par 2 instructions INSERT dans une transaction différée (en supposant que les inserts procèdent sans erreur, bien sûr).

Voici un exemple où nous voulons forcer une annulation de la transaction.

conn.executeTransaction(function* complexTransaction() {
  yield conn.execute("INSERT INTO myTable VALUES (?, ?)", ["foo", "bar"]);
  let data = yield conn.execute("SELECT * FROM myTable");
  if (data.length < 5) {
    throw new Error("We don't have the expected 5 rows to perform the next operation!");
  }

  // ...
});

Sélection et retour des données

Ces exemples montrent comment accéder aux données qui sont retournées.

Cet exemple montre plusieurs lignes d'une table retournées en utilisant le paramètre onRow.

let accounts = [];
let accountId, userName;
    
let statement = "SELECT account_id, username FROM accounts ORDER BY username ASC";
    
conn.executeCached(statement, null, function(row) {
  accountId = row.getResultByName("account_id");
  userName = row.getResultByName("username");
  accounts.push({ accountId: accountId, userName: userName });
}).then(function onStatementComplete(result) {
  // All accounts returned successfully, so do something with them.
  console.log(result); // It worked!
  if (callback) {
    callback(null, accounts);
  }
},
function onError(err) {
  // An error occurred.
  console.log(err); // Error, Oh noes!
  if (callback) {
    callback(err);
  }
});

Remarque: les> paramètres then peuvent être des fonctions anonymes (i.e. function() ) , les seulements nomées sont onStatementComplete et onError pour la lisibilité.

Cet exemple démontre la récupération d'une ligne sans utiliser le paramètre onRow, en utilisant le résultat de conn.execute. Cet exemple montre également la récupération de la clé primaire de la dernière ligne insérée.

Task.spawn(function* () {
  try {
    conn = yield Sqlite.openConnection({ path: dbFile.path });

    let statement = "INSERT INTO accounts (username, details) VALUES (:username, :details)"
    let params = { username:"LordBusiness", details: "All I'm asking for is total perfection." };

    yield conn.execute(statement,params);

    // Get accountId of the INSERT.
    statement = "SELECT last_insert_rowid() AS lastInsertRowID";
    result = yield conn.execute(statement);
        
    // Only one row is returned.
    let row = result[0];
    let accountId = row.getResultByName("lastInsertRowID");

    if (callback) {
      callback(null, accountId);
    }
  } catch (err) {
    if (callback) {
      callback(err);
    }
  } finally {
    conn.close();
  }
});

Remarque: La valeur retournée par last_insert_rowid() l'est par connexion, de sorte que vous devrez peut-être ouvrir des connexions séparées lorsque vous faites plusieurs INSERT à différents endroits, pour être sûr que l'identifiant de ligne qui est retourné corresponde.

Voir aussi

Étiquettes et contributeurs liés au document

 Contributeurs à cette page : jmh
 Dernière mise à jour par : jmh,