toolkit/modules/tests/xpcshell/test_sqlite.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* Any copyright is dedicated to the Public Domain.
     2  * http://creativecommons.org/publicdomain/zero/1.0/ */
     4 "use strict";
     6 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
     8 do_get_profile();
    10 Cu.import("resource://gre/modules/Promise.jsm");
    11 Cu.import("resource://gre/modules/osfile.jsm");
    12 Cu.import("resource://gre/modules/FileUtils.jsm");
    13 Cu.import("resource://gre/modules/Services.jsm");
    14 Cu.import("resource://gre/modules/Sqlite.jsm");
    15 Cu.import("resource://gre/modules/Task.jsm");
    16 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    18 // To spin the event loop in test.
    19 Cu.import("resource://services-common/async.js");
    21 function sleep(ms) {
    22   let deferred = Promise.defer();
    24   let timer = Cc["@mozilla.org/timer;1"]
    25                 .createInstance(Ci.nsITimer);
    27   timer.initWithCallback({
    28     notify: function () {
    29       deferred.resolve();
    30     },
    31   }, ms, timer.TYPE_ONE_SHOT);
    33   return deferred.promise;
    34 }
    36 function getConnection(dbName, extraOptions={}) {
    37   let path = dbName + ".sqlite";
    38   let options = {path: path};
    39   for (let [k, v] in Iterator(extraOptions)) {
    40     options[k] = v;
    41   }
    43   return Sqlite.openConnection(options);
    44 }
    46 function getDummyDatabase(name, extraOptions={}) {
    47   const TABLES = {
    48     dirs: "id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT",
    49     files: "id INTEGER PRIMARY KEY AUTOINCREMENT, dir_id INTEGER, path TEXT",
    50   };
    52   let c = yield getConnection(name, extraOptions);
    53   c._initialStatementCount = 0;
    55   for (let [k, v] in Iterator(TABLES)) {
    56     yield c.execute("CREATE TABLE " + k + "(" + v + ")");
    57     c._initialStatementCount++;
    58   }
    60   throw new Task.Result(c);
    61 }
    63 function getDummyTempDatabase(name, extraOptions={}) {
    64   const TABLES = {
    65     dirs: "id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT",
    66     files: "id INTEGER PRIMARY KEY AUTOINCREMENT, dir_id INTEGER, path TEXT",
    67   };
    69   let c = yield getConnection(name, extraOptions);
    70   c._initialStatementCount = 0;
    72   for (let [k, v] in Iterator(TABLES)) {
    73     yield c.execute("CREATE TEMP TABLE " + k + "(" + v + ")");
    74     c._initialStatementCount++;
    75   }
    77   throw new Task.Result(c);
    78 }
    80 function run_test() {
    81   Cu.import("resource://testing-common/services-common/logging.js");
    82   initTestLogging("Trace");
    84   run_next_test();
    85 }
    87 add_task(function test_open_normal() {
    88   let c = yield Sqlite.openConnection({path: "test_open_normal.sqlite"});
    89   yield c.close();
    90 });
    92 add_task(function test_open_unshared() {
    93   let path = OS.Path.join(OS.Constants.Path.profileDir, "test_open_unshared.sqlite");
    95   let c = yield Sqlite.openConnection({path: path, sharedMemoryCache: false});
    96   yield c.close();
    97 });
    99 add_task(function test_get_dummy_database() {
   100   let db = yield getDummyDatabase("get_dummy_database");
   102   do_check_eq(typeof(db), "object");
   103   yield db.close();
   104 });
   106 add_task(function test_schema_version() {
   107   let db = yield getDummyDatabase("schema_version");
   109   let version = yield db.getSchemaVersion();
   110   do_check_eq(version, 0);
   112   db.setSchemaVersion(14);
   113   version = yield db.getSchemaVersion();
   114   do_check_eq(version, 14);
   116   for (let v of [0.5, "foobar", NaN]) {
   117     let success;
   118     try {
   119       yield db.setSchemaVersion(v);
   120       do_print("Schema version " + v + " should have been rejected");
   121       success = false;
   122     } catch (ex if ex.message.startsWith("Schema version must be an integer.")) {
   123       success = true;
   124     }
   125     do_check_true(success);
   127     version = yield db.getSchemaVersion();
   128     do_check_eq(version, 14);
   129   }
   131   yield db.close();
   132 });
   134 add_task(function test_simple_insert() {
   135   let c = yield getDummyDatabase("simple_insert");
   137   let result = yield c.execute("INSERT INTO dirs VALUES (NULL, 'foo')");
   138   do_check_true(Array.isArray(result));
   139   do_check_eq(result.length, 0);
   140   yield c.close();
   141 });
   143 add_task(function test_simple_bound_array() {
   144   let c = yield getDummyDatabase("simple_bound_array");
   146   let result = yield c.execute("INSERT INTO dirs VALUES (?, ?)", [1, "foo"]);
   147   do_check_eq(result.length, 0);
   148   yield c.close();
   149 });
   151 add_task(function test_simple_bound_object() {
   152   let c = yield getDummyDatabase("simple_bound_object");
   153   let result = yield c.execute("INSERT INTO dirs VALUES (:id, :path)",
   154                                {id: 1, path: "foo"});
   155   do_check_eq(result.length, 0);
   156   result = yield c.execute("SELECT id, path FROM dirs");
   157   do_check_eq(result.length, 1);
   158   do_check_eq(result[0].getResultByName("id"), 1);
   159   do_check_eq(result[0].getResultByName("path"), "foo");
   160   yield c.close();
   161 });
   163 // This is mostly a sanity test to ensure simple executions work.
   164 add_task(function test_simple_insert_then_select() {
   165   let c = yield getDummyDatabase("simple_insert_then_select");
   167   yield c.execute("INSERT INTO dirs VALUES (NULL, 'foo')");
   168   yield c.execute("INSERT INTO dirs (path) VALUES (?)", ["bar"]);
   170   let result = yield c.execute("SELECT * FROM dirs");
   171   do_check_eq(result.length, 2);
   173   let i = 0;
   174   for (let row of result) {
   175     i++;
   177     do_check_eq(row.numEntries, 2);
   178     do_check_eq(row.getResultByIndex(0), i);
   180     let expected = {1: "foo", 2: "bar"}[i];
   181     do_check_eq(row.getResultByName("path"), expected);
   182   }
   184   yield c.close();
   185 });
   187 add_task(function test_repeat_execution() {
   188   let c = yield getDummyDatabase("repeat_execution");
   190   let sql = "INSERT INTO dirs (path) VALUES (:path)";
   191   yield c.executeCached(sql, {path: "foo"});
   192   yield c.executeCached(sql);
   194   let result = yield c.execute("SELECT * FROM dirs");
   196   do_check_eq(result.length, 2);
   198   yield c.close();
   199 });
   201 add_task(function test_table_exists() {
   202   let c = yield getDummyDatabase("table_exists");
   204   do_check_false(yield c.tableExists("does_not_exist"));
   205   do_check_true(yield c.tableExists("dirs"));
   206   do_check_true(yield c.tableExists("files"));
   208   yield c.close();
   209 });
   211 add_task(function test_index_exists() {
   212   let c = yield getDummyDatabase("index_exists");
   214   do_check_false(yield c.indexExists("does_not_exist"));
   216   yield c.execute("CREATE INDEX my_index ON dirs (path)");
   217   do_check_true(yield c.indexExists("my_index"));
   219   yield c.close();
   220 });
   222 add_task(function test_temp_table_exists() {
   223   let c = yield getDummyTempDatabase("temp_table_exists");
   225   do_check_false(yield c.tableExists("temp_does_not_exist"));
   226   do_check_true(yield c.tableExists("dirs"));
   227   do_check_true(yield c.tableExists("files"));
   229   yield c.close();
   230 });
   232 add_task(function test_temp_index_exists() {
   233   let c = yield getDummyTempDatabase("temp_index_exists");
   235   do_check_false(yield c.indexExists("temp_does_not_exist"));
   237   yield c.execute("CREATE INDEX my_index ON dirs (path)");
   238   do_check_true(yield c.indexExists("my_index"));
   240   yield c.close();
   241 });
   243 add_task(function test_close_cached() {
   244   let c = yield getDummyDatabase("close_cached");
   246   yield c.executeCached("SELECT * FROM dirs");
   247   yield c.executeCached("SELECT * FROM files");
   249   yield c.close();
   250 });
   252 add_task(function test_execute_invalid_statement() {
   253   let c = yield getDummyDatabase("invalid_statement");
   255   let deferred = Promise.defer();
   257   do_check_eq(c._anonymousStatements.size, 0);
   259   c.execute("SELECT invalid FROM unknown").then(do_throw, function onError(error) {
   260     deferred.resolve();
   261   });
   263   yield deferred.promise;
   265   // Ensure we don't leak the statement instance.
   266   do_check_eq(c._anonymousStatements.size, 0);
   268   yield c.close();
   269 });
   271 add_task(function test_on_row_exception_ignored() {
   272   let c = yield getDummyDatabase("on_row_exception_ignored");
   274   let sql = "INSERT INTO dirs (path) VALUES (?)";
   275   for (let i = 0; i < 10; i++) {
   276     yield c.executeCached(sql, ["dir" + i]);
   277   }
   279   let i = 0;
   280   yield c.execute("SELECT * FROM DIRS", null, function onRow(row) {
   281     i++;
   283     throw new Error("Some silly error.");
   284   });
   286   do_check_eq(i, 10);
   288   yield c.close();
   289 });
   291 // Ensure StopIteration during onRow causes processing to stop.
   292 add_task(function test_on_row_stop_iteration() {
   293   let c = yield getDummyDatabase("on_row_stop_iteration");
   295   let sql = "INSERT INTO dirs (path) VALUES (?)";
   296   for (let i = 0; i < 10; i++) {
   297     yield c.executeCached(sql, ["dir" + i]);
   298   }
   300   let i = 0;
   301   let result = yield c.execute("SELECT * FROM dirs", null, function onRow(row) {
   302     i++;
   304     if (i == 5) {
   305       throw StopIteration;
   306     }
   307   });
   309   do_check_null(result);
   310   do_check_eq(i, 5);
   312   yield c.close();
   313 });
   315 add_task(function test_invalid_transaction_type() {
   316   let c = yield getDummyDatabase("invalid_transaction_type");
   318   let errored = false;
   319   try {
   320     c.executeTransaction(function () {}, "foobar");
   321   } catch (ex) {
   322     errored = true;
   323     do_check_true(ex.message.startsWith("Unknown transaction type"));
   324   } finally {
   325     do_check_true(errored);
   326   }
   328   yield c.close();
   329 });
   331 add_task(function test_execute_transaction_success() {
   332   let c = yield getDummyDatabase("execute_transaction_success");
   334   do_check_false(c.transactionInProgress);
   336   yield c.executeTransaction(function transaction(conn) {
   337     do_check_eq(c, conn);
   338     do_check_true(conn.transactionInProgress);
   340     yield conn.execute("INSERT INTO dirs (path) VALUES ('foo')");
   341   });
   343   do_check_false(c.transactionInProgress);
   344   let rows = yield c.execute("SELECT * FROM dirs");
   345   do_check_true(Array.isArray(rows));
   346   do_check_eq(rows.length, 1);
   348   yield c.close();
   349 });
   351 add_task(function test_execute_transaction_rollback() {
   352   let c = yield getDummyDatabase("execute_transaction_rollback");
   354   let deferred = Promise.defer();
   356   c.executeTransaction(function transaction(conn) {
   357     yield conn.execute("INSERT INTO dirs (path) VALUES ('foo')");
   358     print("Expecting error with next statement.");
   359     yield conn.execute("INSERT INTO invalid VALUES ('foo')");
   361     // We should never get here.
   362     do_throw();
   363   }).then(do_throw, function onError(error) {
   364     deferred.resolve();
   365   });
   367   yield deferred.promise;
   369   let rows = yield c.execute("SELECT * FROM dirs");
   370   do_check_eq(rows.length, 0);
   372   yield c.close();
   373 });
   375 add_task(function test_close_during_transaction() {
   376   let c = yield getDummyDatabase("close_during_transaction");
   378   yield c.execute("INSERT INTO dirs (path) VALUES ('foo')");
   380   let errored = false;
   381   try {
   382     yield c.executeTransaction(function transaction(conn) {
   383       yield c.execute("INSERT INTO dirs (path) VALUES ('bar')");
   384       yield c.close();
   385     });
   386   } catch (ex) {
   387     errored = true;
   388     do_check_eq(ex.message, "Connection being closed.");
   389   } finally {
   390     do_check_true(errored);
   391   }
   393   let c2 = yield getConnection("close_during_transaction");
   394   let rows = yield c2.execute("SELECT * FROM dirs");
   395   do_check_eq(rows.length, 1);
   397   yield c2.close();
   398 });
   400 add_task(function test_detect_multiple_transactions() {
   401   let c = yield getDummyDatabase("detect_multiple_transactions");
   403   yield c.executeTransaction(function main() {
   404     yield c.execute("INSERT INTO dirs (path) VALUES ('foo')");
   406     let errored = false;
   407     try {
   408       yield c.executeTransaction(function child() {
   409         yield c.execute("INSERT INTO dirs (path) VALUES ('bar')");
   410       });
   411     } catch (ex) {
   412       errored = true;
   413       do_check_true(ex.message.startsWith("A transaction is already active."));
   414     } finally {
   415       do_check_true(errored);
   416     }
   417   });
   419   let rows = yield c.execute("SELECT * FROM dirs");
   420   do_check_eq(rows.length, 1);
   422   yield c.close();
   423 });
   425 add_task(function test_shrink_memory() {
   426   let c = yield getDummyDatabase("shrink_memory");
   428   // It's just a simple sanity test. We have no way of measuring whether this
   429   // actually does anything.
   431   yield c.shrinkMemory();
   432   yield c.close();
   433 });
   435 add_task(function test_no_shrink_on_init() {
   436   let c = yield getConnection("no_shrink_on_init",
   437                               {shrinkMemoryOnConnectionIdleMS: 200});
   439   let oldShrink = c.shrinkMemory;
   440   let count = 0;
   441   Object.defineProperty(c, "shrinkMemory", {
   442     value: function () {
   443       count++;
   444     },
   445   });
   447   // We should not shrink until a statement has been executed.
   448   yield sleep(220);
   449   do_check_eq(count, 0);
   451   yield c.execute("SELECT 1");
   452   yield sleep(220);
   453   do_check_eq(count, 1);
   455   yield c.close();
   456 });
   458 add_task(function test_idle_shrink_fires() {
   459   let c = yield getDummyDatabase("idle_shrink_fires",
   460                                  {shrinkMemoryOnConnectionIdleMS: 200});
   461   c._clearIdleShrinkTimer();
   463   let oldShrink = c.shrinkMemory;
   464   let shrinkPromises = [];
   466   let count = 0;
   467   Object.defineProperty(c, "shrinkMemory", {
   468     value: function () {
   469       count++;
   470       let promise = oldShrink.call(c);
   471       shrinkPromises.push(promise);
   472       return promise;
   473     },
   474   });
   476   // We reset the idle shrink timer after monkeypatching because otherwise the
   477   // installed timer callback will reference the non-monkeypatched function.
   478   c._startIdleShrinkTimer();
   480   yield sleep(220);
   481   do_check_eq(count, 1);
   482   do_check_eq(shrinkPromises.length, 1);
   483   yield shrinkPromises[0];
   484   shrinkPromises.shift();
   486   // We shouldn't shrink again unless a statement was executed.
   487   yield sleep(300);
   488   do_check_eq(count, 1);
   490   yield c.execute("SELECT 1");
   491   yield sleep(300);
   493   do_check_eq(count, 2);
   494   do_check_eq(shrinkPromises.length, 1);
   495   yield shrinkPromises[0];
   497   yield c.close();
   498 });
   500 add_task(function test_idle_shrink_reset_on_operation() {
   501   const INTERVAL = 500;
   502   let c = yield getDummyDatabase("idle_shrink_reset_on_operation",
   503                                  {shrinkMemoryOnConnectionIdleMS: INTERVAL});
   505   c._clearIdleShrinkTimer();
   507   let oldShrink = c.shrinkMemory;
   508   let shrinkPromises = [];
   509   let count = 0;
   511   Object.defineProperty(c, "shrinkMemory", {
   512     value: function () {
   513       count++;
   514       let promise = oldShrink.call(c);
   515       shrinkPromises.push(promise);
   516       return promise;
   517     },
   518   });
   520   let now = new Date();
   521   c._startIdleShrinkTimer();
   523   let initialIdle = new Date(now.getTime() + INTERVAL);
   525   // Perform database operations until initial scheduled time has been passed.
   526   let i = 0;
   527   while (new Date() < initialIdle) {
   528     yield c.execute("INSERT INTO dirs (path) VALUES (?)", ["" + i]);
   529     i++;
   530   }
   532   do_check_true(i > 0);
   534   // We should not have performed an idle while doing operations.
   535   do_check_eq(count, 0);
   537   // Wait for idle timer.
   538   yield sleep(INTERVAL);
   540   // Ensure we fired.
   541   do_check_eq(count, 1);
   542   do_check_eq(shrinkPromises.length, 1);
   543   yield shrinkPromises[0];
   545   yield c.close();
   546 });
   548 add_task(function test_in_progress_counts() {
   549   let c = yield getDummyDatabase("in_progress_counts");
   550   do_check_eq(c._statementCounter, c._initialStatementCount);
   551   do_check_eq(c._pendingStatements.size, 0);
   552   yield c.executeCached("INSERT INTO dirs (path) VALUES ('foo')");
   553   do_check_eq(c._statementCounter, c._initialStatementCount + 1);
   554   do_check_eq(c._pendingStatements.size, 0);
   556   let expectOne;
   557   let expectTwo;
   559   // Please forgive me.
   560   let inner = Async.makeSpinningCallback();
   561   let outer = Async.makeSpinningCallback();
   563   // We want to make sure that two queries executing simultaneously
   564   // result in `_pendingStatements.size` reaching 2, then dropping back to 0.
   565   //
   566   // To do so, we kick off a second statement within the row handler
   567   // of the first, then wait for both to finish.
   569   yield c.executeCached("SELECT * from dirs", null, function onRow() {
   570     // In the onRow handler, we're still an outstanding query.
   571     // Expect a single in-progress entry.
   572     expectOne = c._pendingStatements.size;
   574     // Start another query, checking that after its statement has been created
   575     // there are two statements in progress.
   576     let p = c.executeCached("SELECT 10, path from dirs");
   577     expectTwo = c._pendingStatements.size;
   579     // Now wait for it to be done before we return from the row handler …
   580     p.then(function onInner() {
   581       inner();
   582     });
   583   }).then(function onOuter() {
   584     // … and wait for the inner to be done before we finish …
   585     inner.wait();
   586     outer();
   587   });
   589   // … and wait for both queries to have finished before we go on and
   590   // test postconditions.
   591   outer.wait();
   593   do_check_eq(expectOne, 1);
   594   do_check_eq(expectTwo, 2);
   595   do_check_eq(c._statementCounter, c._initialStatementCount + 3);
   596   do_check_eq(c._pendingStatements.size, 0);
   598   yield c.close();
   599 });
   601 add_task(function test_discard_while_active() {
   602   let c = yield getDummyDatabase("discard_while_active");
   604   yield c.executeCached("INSERT INTO dirs (path) VALUES ('foo')");
   605   yield c.executeCached("INSERT INTO dirs (path) VALUES ('bar')");
   607   let discarded = -1;
   608   let first = true;
   609   let sql = "SELECT * FROM dirs";
   610   yield c.executeCached(sql, null, function onRow(row) {
   611     if (!first) {
   612       return;
   613     }
   614     first = false;
   615     discarded = c.discardCachedStatements();
   616   });
   618   // We discarded everything, because the SELECT had already started to run.
   619   do_check_eq(3, discarded);
   621   // And again is safe.
   622   do_check_eq(0, c.discardCachedStatements());
   624   yield c.close();
   625 });
   627 add_task(function test_discard_cached() {
   628   let c = yield getDummyDatabase("discard_cached");
   630   yield c.executeCached("SELECT * from dirs");
   631   do_check_eq(1, c._cachedStatements.size);
   633   yield c.executeCached("SELECT * from files");
   634   do_check_eq(2, c._cachedStatements.size);
   636   yield c.executeCached("SELECT * from dirs");
   637   do_check_eq(2, c._cachedStatements.size);
   639   c.discardCachedStatements();
   640   do_check_eq(0, c._cachedStatements.size);
   642   yield c.close();
   643 });
   645 add_task(function test_programmatic_binding() {
   646   let c = yield getDummyDatabase("programmatic_binding");
   648   let bindings = [
   649     {id: 1,    path: "foobar"},
   650     {id: null, path: "baznoo"},
   651     {id: 5,    path: "toofoo"},
   652   ];
   654   let sql = "INSERT INTO dirs VALUES (:id, :path)";
   655   let result = yield c.execute(sql, bindings);
   656   do_check_eq(result.length, 0);
   658   let rows = yield c.executeCached("SELECT * from dirs");
   659   do_check_eq(rows.length, 3);
   660   yield c.close();
   661 });
   663 add_task(function test_programmatic_binding_transaction() {
   664   let c = yield getDummyDatabase("programmatic_binding_transaction");
   666   let bindings = [
   667     {id: 1,    path: "foobar"},
   668     {id: null, path: "baznoo"},
   669     {id: 5,    path: "toofoo"},
   670   ];
   672   let sql = "INSERT INTO dirs VALUES (:id, :path)";
   673   yield c.executeTransaction(function transaction() {
   674     let result = yield c.execute(sql, bindings);
   675     do_check_eq(result.length, 0);
   677     let rows = yield c.executeCached("SELECT * from dirs");
   678     do_check_eq(rows.length, 3);
   679   });
   681   // Transaction committed.
   682   let rows = yield c.executeCached("SELECT * from dirs");
   683   do_check_eq(rows.length, 3);
   684   yield c.close();
   685 });
   687 add_task(function test_programmatic_binding_transaction_partial_rollback() {
   688   let c = yield getDummyDatabase("programmatic_binding_transaction_partial_rollback");
   690   let bindings = [
   691     {id: 2, path: "foobar"},
   692     {id: 3, path: "toofoo"},
   693   ];
   695   let sql = "INSERT INTO dirs VALUES (:id, :path)";
   697   // Add some data in an implicit transaction before beginning the batch insert.
   698   yield c.execute(sql, {id: 1, path: "works"});
   700   let secondSucceeded = false;
   701   try {
   702     yield c.executeTransaction(function transaction() {
   703       // Insert one row. This won't implicitly start a transaction.
   704       let result = yield c.execute(sql, bindings[0]);
   706       // Insert multiple rows. mozStorage will want to start a transaction.
   707       // One of the inserts will fail, so the transaction should be rolled back.
   708       let result = yield c.execute(sql, bindings);
   709       secondSucceeded = true;
   710     });
   711   } catch (ex) {
   712     print("Caught expected exception: " + ex);
   713   }
   715   // We did not get to the end of our in-transaction block.
   716   do_check_false(secondSucceeded);
   718   // Everything that happened in *our* transaction, not mozStorage's, got
   719   // rolled back, but the first row still exists.
   720   let rows = yield c.executeCached("SELECT * from dirs");
   721   do_check_eq(rows.length, 1);
   722   do_check_eq(rows[0].getResultByName("path"), "works");
   723   yield c.close();
   724 });
   726 /**
   727  * Just like the previous test, but relying on the implicit
   728  * transaction established by mozStorage.
   729  */
   730 add_task(function test_programmatic_binding_implicit_transaction() {
   731   let c = yield getDummyDatabase("programmatic_binding_implicit_transaction");
   733   let bindings = [
   734     {id: 2, path: "foobar"},
   735     {id: 1, path: "toofoo"},
   736   ];
   738   let sql = "INSERT INTO dirs VALUES (:id, :path)";
   739   let secondSucceeded = false;
   740   yield c.execute(sql, {id: 1, path: "works"});
   741   try {
   742     let result = yield c.execute(sql, bindings);
   743     secondSucceeded = true;
   744   } catch (ex) {
   745     print("Caught expected exception: " + ex);
   746   }
   748   do_check_false(secondSucceeded);
   750   // The entire batch failed.
   751   let rows = yield c.executeCached("SELECT * from dirs");
   752   do_check_eq(rows.length, 1);
   753   do_check_eq(rows[0].getResultByName("path"), "works");
   754   yield c.close();
   755 });
   757 /**
   758  * Test that direct binding of params and execution through mozStorage doesn't
   759  * error when we manually create a transaction. See Bug 856925.
   760  */
   761 add_task(function test_direct() {
   762   let file = FileUtils.getFile("TmpD", ["test_direct.sqlite"]);
   763   file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
   764   print("Opening " + file.path);
   766   let db = Services.storage.openDatabase(file);
   767   print("Opened " + db);
   769   db.executeSimpleSQL("CREATE TABLE types (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, UNIQUE (name))");
   770   print("Executed setup.");
   772   let statement = db.createAsyncStatement("INSERT INTO types (name) VALUES (:name)");
   773   let params = statement.newBindingParamsArray();
   774   let one = params.newBindingParams();
   775   one.bindByName("name", null);
   776   params.addParams(one);
   777   let two = params.newBindingParams();
   778   two.bindByName("name", "bar");
   779   params.addParams(two);
   781   print("Beginning transaction.");
   782   let begin = db.createAsyncStatement("BEGIN DEFERRED TRANSACTION");
   783   let end = db.createAsyncStatement("COMMIT TRANSACTION");
   785   let deferred = Promise.defer();
   786   begin.executeAsync({
   787     handleCompletion: function (reason) {
   788       deferred.resolve();
   789     }
   790   });
   791   yield deferred.promise;
   793   statement.bindParameters(params);
   795   deferred = Promise.defer();
   796   print("Executing async.");
   797   statement.executeAsync({
   798     handleResult: function (resultSet) {
   799     },
   801     handleError:  function (error) {
   802       print("Error when executing SQL (" + error.result + "): " +
   803             error.message);
   804       print("Original error: " + error.error);
   805       errors.push(error);
   806       deferred.reject();
   807     },
   809     handleCompletion: function (reason) {
   810       print("Completed.");
   811       deferred.resolve();
   812     }
   813   });
   815   yield deferred.promise;
   817   deferred = Promise.defer();
   818   end.executeAsync({
   819     handleCompletion: function (reason) {
   820       deferred.resolve();
   821     }
   822   });
   823   yield deferred.promise;
   825   statement.finalize();
   826   begin.finalize();
   827   end.finalize();
   829   deferred = Promise.defer();
   830   db.asyncClose(function () {
   831     deferred.resolve()
   832   });
   833   yield deferred.promise;
   834 });
   836 /**
   837  * Test Sqlite.cloneStorageConnection.
   838  */
   839 add_task(function* test_cloneStorageConnection() {
   840   let file = new FileUtils.File(OS.Path.join(OS.Constants.Path.profileDir,
   841                                              "test_cloneStorageConnection.sqlite"));
   842   let c = yield new Promise((success, failure) => {
   843     Services.storage.openAsyncDatabase(file, null, (status, db) => {
   844       if (Components.isSuccessCode(status)) {
   845         success(db.QueryInterface(Ci.mozIStorageAsyncConnection));
   846       } else {
   847         failure(new Error(status));
   848       }
   849     });
   850   });
   852   let clone = yield Sqlite.cloneStorageConnection({ connection: c, readOnly: true });
   853   // Just check that it works.
   854   yield clone.execute("SELECT 1");
   856   let clone2 = yield Sqlite.cloneStorageConnection({ connection: c, readOnly: false });
   857   // Just check that it works.
   858   yield clone2.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)");
   860   // Closing order should not matter.
   861   yield c.asyncClose();
   862   yield clone2.close();
   863   yield clone.close();
   864 });
   866 /**
   867  * Test Sqlite.cloneStorageConnection invalid argument.
   868  */
   869 add_task(function* test_cloneStorageConnection() {
   870   try {
   871     let clone = yield Sqlite.cloneStorageConnection({ connection: null });
   872     do_throw(new Error("Should throw on invalid connection"));
   873   } catch (ex if ex.name == "TypeError") {}
   874 });
   876 /**
   877  * Test clone() method.
   878  */
   879 add_task(function* test_clone() {
   880   let c = yield getDummyDatabase("clone");
   882   let clone = yield c.clone();
   883   // Just check that it works.
   884   yield clone.execute("SELECT 1");
   885   // Closing order should not matter.
   886   yield c.close();
   887   yield clone.close();
   888 });
   890 /**
   891  * Test clone(readOnly) method.
   892  */
   893 add_task(function* test_readOnly_clone() {
   894   let path = OS.Path.join(OS.Constants.Path.profileDir, "test_readOnly_clone.sqlite");
   895   let c = yield Sqlite.openConnection({path: path, sharedMemoryCache: false});
   897   let clone = yield c.clone(true);
   898   // Just check that it works.
   899   yield clone.execute("SELECT 1");
   900   // But should not be able to write.
   901   try {
   902     yield clone.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)");
   903     do_throw(new Error("Should not be able to write to a read-only clone."));
   904   } catch (ex) {}
   905   // Closing order should not matter.
   906   yield c.close();
   907   yield clone.close();
   908 });

mercurial