Bug 1247602 - Allow to bind blobs in Sqlite.jsm. r=mak draft
authorIlya Gordeev <mirraz1@rambler.ru>
Fri, 19 Feb 2016 14:24:54 +0500
changeset 464629 b17fcc66e2e0d27674aa16453e592cfd4f93a8a7
parent 463663 168ea3e9ff0bad286676173263613703187ab8f1
child 542952 8080c65da4827a49b04ef1fe1eb16d331a308181
push id42388
push usermak77@bonardo.net
push dateSat, 21 Jan 2017 13:35:14 +0000
reviewersmak
bugs1247602
milestone53.0a1
Bug 1247602 - Allow to bind blobs in Sqlite.jsm. r=mak MozReview-Commit-ID: 2yPO6kidqfn
toolkit/modules/Sqlite.jsm
toolkit/modules/tests/xpcshell/test_sqlite.js
--- a/toolkit/modules/Sqlite.jsm
+++ b/toolkit/modules/Sqlite.jsm
@@ -674,43 +674,51 @@ ConnectionData.prototype = Object.freeze
    * Helper method to bind parameters of various kinds through
    * reflection.
    */
   _bindParameters(statement, params) {
     if (!params) {
       return;
     }
 
+    function bindParam(obj, key, val) {
+      let isBlob = Array.isArray(val);
+      let args = [key, val].concat(isBlob ? [val.length] : []);
+      let methodName =
+        `bind${isBlob ? "Blob" : ""}By${typeof key == "number" ? "Index" : "Name"}`;
+      obj[methodName](...args);
+    }
+
     if (Array.isArray(params)) {
       // It's an array of separate params.
       if (params.length && (typeof(params[0]) == "object")) {
         let paramsArray = statement.newBindingParamsArray();
         for (let p of params) {
           let bindings = paramsArray.newBindingParams();
           for (let [key, value] of Object.entries(p)) {
-            bindings.bindByName(key, value);
+            bindParam(bindings, key, value);
           }
           paramsArray.addParams(bindings);
         }
 
         statement.bindParameters(paramsArray);
         return;
       }
 
       // Indexed params.
       for (let i = 0; i < params.length; i++) {
-        statement.bindByIndex(i, params[i]);
+        bindParam(statement, i, params[i]);
       }
       return;
     }
 
     // Named params.
     if (params && typeof(params) == "object") {
       for (let k in params) {
-        statement.bindByName(k, params[k]);
+        bindParam(statement, k, params[k]);
       }
       return;
     }
 
     throw new Error("Invalid type for bound parameters. Expected Array or " +
                     "object. Got: " + params);
   },
 
--- a/toolkit/modules/tests/xpcshell/test_sqlite.js
+++ b/toolkit/modules/tests/xpcshell/test_sqlite.js
@@ -1,11 +1,8 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
 "use strict";
 
 var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 do_get_profile();
 
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/PromiseUtils.jsm");
@@ -79,22 +76,20 @@ function* getDummyTempDatabase(name, ext
   for (let [k, v] of Object.entries(TABLES)) {
     yield c.execute("CREATE TEMP TABLE " + k + "(" + v + ")");
     c._initialStatementCount++;
   }
 
   return c;
 }
 
-function run_test() {
+add_task(function* test_setup() {
   Cu.import("resource://testing-common/services/common/logging.js");
   initTestLogging("Trace");
-
-  run_next_test();
-}
+});
 
 add_task(function* test_open_normal() {
   let c = yield Sqlite.openConnection({path: "test_open_normal.sqlite"});
   yield c.close();
 });
 
 add_task(function* test_open_unshared() {
   let path = OS.Path.join(OS.Constants.Path.profileDir, "test_open_unshared.sqlite");
@@ -1086,8 +1081,64 @@ add_task(function* test_close_database_o
 
   Components.utils.forceGC();
   Components.utils.forceCC();
   Components.utils.forceShrinkingGC();
 
   yield finalPromise;
   failTestsOnAutoClose(true);
 });
+
+// Test all supported datatypes
+add_task(function* test_datatypes() {
+  let c = yield getConnection("datatypes");
+  yield c.execute("DROP TABLE IF EXISTS datatypes");
+  yield c.execute(`CREATE TABLE datatypes (
+                     null_col    NULL,
+                     integer_col INTEGER NOT NULL,
+                     text_col    TEXT    NOT NULL,
+                     blob_col    BLOB    NOT NULL,
+                     real_col    REAL    NOT NULL,
+                     numeric_col NUMERIC NOT NULL
+                   )`);
+  const bindings = [
+    {
+      null_col: null,
+      integer_col: 12345,
+      text_col: "qwerty",
+      blob_col: new Array(256).fill(undefined).map( (value, index) => index % 256 ),
+      real_col: 3.14159265359,
+      numeric_col: true
+    },
+    {
+      null_col: null,
+      integer_col: -12345,
+      text_col: "",
+      blob_col: new Array(256 * 2).fill(undefined).map( (value, index) => index % 256 ),
+      real_col: Number.NEGATIVE_INFINITY,
+      numeric_col: false
+    }
+  ];
+
+  yield c.execute(`INSERT INTO datatypes VALUES (
+                     :null_col,
+                     :integer_col,
+                     :text_col,
+                     :blob_col,
+                     :real_col,
+                     :numeric_col
+                   )`, bindings);
+
+  let rows = yield c.execute("SELECT * FROM datatypes");
+  Assert.ok(Array.isArray(rows));
+  Assert.equal(rows.length, bindings.length);
+  for (let i = 0 ; i < bindings.length; ++i) {
+    let binding = bindings[i];
+    let row = rows[i];
+    for (let colName in binding) {
+      // In Sqlite bool is stored and then retrieved as numeric.
+      let val = typeof binding[colName] == "boolean" ? +binding[colName]
+                                                       : binding[colName];
+      Assert.deepEqual(val, row.getResultByName(colName));
+    }
+  }
+  yield c.close();
+});