toolkit/modules/tests/xpcshell/test_sqlite.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:5f52ad6e0a79
1 /* Any copyright is dedicated to the Public Domain.
2 * http://creativecommons.org/publicdomain/zero/1.0/ */
3
4 "use strict";
5
6 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
7
8 do_get_profile();
9
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");
17
18 // To spin the event loop in test.
19 Cu.import("resource://services-common/async.js");
20
21 function sleep(ms) {
22 let deferred = Promise.defer();
23
24 let timer = Cc["@mozilla.org/timer;1"]
25 .createInstance(Ci.nsITimer);
26
27 timer.initWithCallback({
28 notify: function () {
29 deferred.resolve();
30 },
31 }, ms, timer.TYPE_ONE_SHOT);
32
33 return deferred.promise;
34 }
35
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 }
42
43 return Sqlite.openConnection(options);
44 }
45
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 };
51
52 let c = yield getConnection(name, extraOptions);
53 c._initialStatementCount = 0;
54
55 for (let [k, v] in Iterator(TABLES)) {
56 yield c.execute("CREATE TABLE " + k + "(" + v + ")");
57 c._initialStatementCount++;
58 }
59
60 throw new Task.Result(c);
61 }
62
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 };
68
69 let c = yield getConnection(name, extraOptions);
70 c._initialStatementCount = 0;
71
72 for (let [k, v] in Iterator(TABLES)) {
73 yield c.execute("CREATE TEMP TABLE " + k + "(" + v + ")");
74 c._initialStatementCount++;
75 }
76
77 throw new Task.Result(c);
78 }
79
80 function run_test() {
81 Cu.import("resource://testing-common/services-common/logging.js");
82 initTestLogging("Trace");
83
84 run_next_test();
85 }
86
87 add_task(function test_open_normal() {
88 let c = yield Sqlite.openConnection({path: "test_open_normal.sqlite"});
89 yield c.close();
90 });
91
92 add_task(function test_open_unshared() {
93 let path = OS.Path.join(OS.Constants.Path.profileDir, "test_open_unshared.sqlite");
94
95 let c = yield Sqlite.openConnection({path: path, sharedMemoryCache: false});
96 yield c.close();
97 });
98
99 add_task(function test_get_dummy_database() {
100 let db = yield getDummyDatabase("get_dummy_database");
101
102 do_check_eq(typeof(db), "object");
103 yield db.close();
104 });
105
106 add_task(function test_schema_version() {
107 let db = yield getDummyDatabase("schema_version");
108
109 let version = yield db.getSchemaVersion();
110 do_check_eq(version, 0);
111
112 db.setSchemaVersion(14);
113 version = yield db.getSchemaVersion();
114 do_check_eq(version, 14);
115
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);
126
127 version = yield db.getSchemaVersion();
128 do_check_eq(version, 14);
129 }
130
131 yield db.close();
132 });
133
134 add_task(function test_simple_insert() {
135 let c = yield getDummyDatabase("simple_insert");
136
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 });
142
143 add_task(function test_simple_bound_array() {
144 let c = yield getDummyDatabase("simple_bound_array");
145
146 let result = yield c.execute("INSERT INTO dirs VALUES (?, ?)", [1, "foo"]);
147 do_check_eq(result.length, 0);
148 yield c.close();
149 });
150
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 });
162
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");
166
167 yield c.execute("INSERT INTO dirs VALUES (NULL, 'foo')");
168 yield c.execute("INSERT INTO dirs (path) VALUES (?)", ["bar"]);
169
170 let result = yield c.execute("SELECT * FROM dirs");
171 do_check_eq(result.length, 2);
172
173 let i = 0;
174 for (let row of result) {
175 i++;
176
177 do_check_eq(row.numEntries, 2);
178 do_check_eq(row.getResultByIndex(0), i);
179
180 let expected = {1: "foo", 2: "bar"}[i];
181 do_check_eq(row.getResultByName("path"), expected);
182 }
183
184 yield c.close();
185 });
186
187 add_task(function test_repeat_execution() {
188 let c = yield getDummyDatabase("repeat_execution");
189
190 let sql = "INSERT INTO dirs (path) VALUES (:path)";
191 yield c.executeCached(sql, {path: "foo"});
192 yield c.executeCached(sql);
193
194 let result = yield c.execute("SELECT * FROM dirs");
195
196 do_check_eq(result.length, 2);
197
198 yield c.close();
199 });
200
201 add_task(function test_table_exists() {
202 let c = yield getDummyDatabase("table_exists");
203
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"));
207
208 yield c.close();
209 });
210
211 add_task(function test_index_exists() {
212 let c = yield getDummyDatabase("index_exists");
213
214 do_check_false(yield c.indexExists("does_not_exist"));
215
216 yield c.execute("CREATE INDEX my_index ON dirs (path)");
217 do_check_true(yield c.indexExists("my_index"));
218
219 yield c.close();
220 });
221
222 add_task(function test_temp_table_exists() {
223 let c = yield getDummyTempDatabase("temp_table_exists");
224
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"));
228
229 yield c.close();
230 });
231
232 add_task(function test_temp_index_exists() {
233 let c = yield getDummyTempDatabase("temp_index_exists");
234
235 do_check_false(yield c.indexExists("temp_does_not_exist"));
236
237 yield c.execute("CREATE INDEX my_index ON dirs (path)");
238 do_check_true(yield c.indexExists("my_index"));
239
240 yield c.close();
241 });
242
243 add_task(function test_close_cached() {
244 let c = yield getDummyDatabase("close_cached");
245
246 yield c.executeCached("SELECT * FROM dirs");
247 yield c.executeCached("SELECT * FROM files");
248
249 yield c.close();
250 });
251
252 add_task(function test_execute_invalid_statement() {
253 let c = yield getDummyDatabase("invalid_statement");
254
255 let deferred = Promise.defer();
256
257 do_check_eq(c._anonymousStatements.size, 0);
258
259 c.execute("SELECT invalid FROM unknown").then(do_throw, function onError(error) {
260 deferred.resolve();
261 });
262
263 yield deferred.promise;
264
265 // Ensure we don't leak the statement instance.
266 do_check_eq(c._anonymousStatements.size, 0);
267
268 yield c.close();
269 });
270
271 add_task(function test_on_row_exception_ignored() {
272 let c = yield getDummyDatabase("on_row_exception_ignored");
273
274 let sql = "INSERT INTO dirs (path) VALUES (?)";
275 for (let i = 0; i < 10; i++) {
276 yield c.executeCached(sql, ["dir" + i]);
277 }
278
279 let i = 0;
280 yield c.execute("SELECT * FROM DIRS", null, function onRow(row) {
281 i++;
282
283 throw new Error("Some silly error.");
284 });
285
286 do_check_eq(i, 10);
287
288 yield c.close();
289 });
290
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");
294
295 let sql = "INSERT INTO dirs (path) VALUES (?)";
296 for (let i = 0; i < 10; i++) {
297 yield c.executeCached(sql, ["dir" + i]);
298 }
299
300 let i = 0;
301 let result = yield c.execute("SELECT * FROM dirs", null, function onRow(row) {
302 i++;
303
304 if (i == 5) {
305 throw StopIteration;
306 }
307 });
308
309 do_check_null(result);
310 do_check_eq(i, 5);
311
312 yield c.close();
313 });
314
315 add_task(function test_invalid_transaction_type() {
316 let c = yield getDummyDatabase("invalid_transaction_type");
317
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 }
327
328 yield c.close();
329 });
330
331 add_task(function test_execute_transaction_success() {
332 let c = yield getDummyDatabase("execute_transaction_success");
333
334 do_check_false(c.transactionInProgress);
335
336 yield c.executeTransaction(function transaction(conn) {
337 do_check_eq(c, conn);
338 do_check_true(conn.transactionInProgress);
339
340 yield conn.execute("INSERT INTO dirs (path) VALUES ('foo')");
341 });
342
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);
347
348 yield c.close();
349 });
350
351 add_task(function test_execute_transaction_rollback() {
352 let c = yield getDummyDatabase("execute_transaction_rollback");
353
354 let deferred = Promise.defer();
355
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')");
360
361 // We should never get here.
362 do_throw();
363 }).then(do_throw, function onError(error) {
364 deferred.resolve();
365 });
366
367 yield deferred.promise;
368
369 let rows = yield c.execute("SELECT * FROM dirs");
370 do_check_eq(rows.length, 0);
371
372 yield c.close();
373 });
374
375 add_task(function test_close_during_transaction() {
376 let c = yield getDummyDatabase("close_during_transaction");
377
378 yield c.execute("INSERT INTO dirs (path) VALUES ('foo')");
379
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 }
392
393 let c2 = yield getConnection("close_during_transaction");
394 let rows = yield c2.execute("SELECT * FROM dirs");
395 do_check_eq(rows.length, 1);
396
397 yield c2.close();
398 });
399
400 add_task(function test_detect_multiple_transactions() {
401 let c = yield getDummyDatabase("detect_multiple_transactions");
402
403 yield c.executeTransaction(function main() {
404 yield c.execute("INSERT INTO dirs (path) VALUES ('foo')");
405
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 });
418
419 let rows = yield c.execute("SELECT * FROM dirs");
420 do_check_eq(rows.length, 1);
421
422 yield c.close();
423 });
424
425 add_task(function test_shrink_memory() {
426 let c = yield getDummyDatabase("shrink_memory");
427
428 // It's just a simple sanity test. We have no way of measuring whether this
429 // actually does anything.
430
431 yield c.shrinkMemory();
432 yield c.close();
433 });
434
435 add_task(function test_no_shrink_on_init() {
436 let c = yield getConnection("no_shrink_on_init",
437 {shrinkMemoryOnConnectionIdleMS: 200});
438
439 let oldShrink = c.shrinkMemory;
440 let count = 0;
441 Object.defineProperty(c, "shrinkMemory", {
442 value: function () {
443 count++;
444 },
445 });
446
447 // We should not shrink until a statement has been executed.
448 yield sleep(220);
449 do_check_eq(count, 0);
450
451 yield c.execute("SELECT 1");
452 yield sleep(220);
453 do_check_eq(count, 1);
454
455 yield c.close();
456 });
457
458 add_task(function test_idle_shrink_fires() {
459 let c = yield getDummyDatabase("idle_shrink_fires",
460 {shrinkMemoryOnConnectionIdleMS: 200});
461 c._clearIdleShrinkTimer();
462
463 let oldShrink = c.shrinkMemory;
464 let shrinkPromises = [];
465
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 });
475
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();
479
480 yield sleep(220);
481 do_check_eq(count, 1);
482 do_check_eq(shrinkPromises.length, 1);
483 yield shrinkPromises[0];
484 shrinkPromises.shift();
485
486 // We shouldn't shrink again unless a statement was executed.
487 yield sleep(300);
488 do_check_eq(count, 1);
489
490 yield c.execute("SELECT 1");
491 yield sleep(300);
492
493 do_check_eq(count, 2);
494 do_check_eq(shrinkPromises.length, 1);
495 yield shrinkPromises[0];
496
497 yield c.close();
498 });
499
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});
504
505 c._clearIdleShrinkTimer();
506
507 let oldShrink = c.shrinkMemory;
508 let shrinkPromises = [];
509 let count = 0;
510
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 });
519
520 let now = new Date();
521 c._startIdleShrinkTimer();
522
523 let initialIdle = new Date(now.getTime() + INTERVAL);
524
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 }
531
532 do_check_true(i > 0);
533
534 // We should not have performed an idle while doing operations.
535 do_check_eq(count, 0);
536
537 // Wait for idle timer.
538 yield sleep(INTERVAL);
539
540 // Ensure we fired.
541 do_check_eq(count, 1);
542 do_check_eq(shrinkPromises.length, 1);
543 yield shrinkPromises[0];
544
545 yield c.close();
546 });
547
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);
555
556 let expectOne;
557 let expectTwo;
558
559 // Please forgive me.
560 let inner = Async.makeSpinningCallback();
561 let outer = Async.makeSpinningCallback();
562
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.
568
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;
573
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;
578
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 });
588
589 // … and wait for both queries to have finished before we go on and
590 // test postconditions.
591 outer.wait();
592
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);
597
598 yield c.close();
599 });
600
601 add_task(function test_discard_while_active() {
602 let c = yield getDummyDatabase("discard_while_active");
603
604 yield c.executeCached("INSERT INTO dirs (path) VALUES ('foo')");
605 yield c.executeCached("INSERT INTO dirs (path) VALUES ('bar')");
606
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 });
617
618 // We discarded everything, because the SELECT had already started to run.
619 do_check_eq(3, discarded);
620
621 // And again is safe.
622 do_check_eq(0, c.discardCachedStatements());
623
624 yield c.close();
625 });
626
627 add_task(function test_discard_cached() {
628 let c = yield getDummyDatabase("discard_cached");
629
630 yield c.executeCached("SELECT * from dirs");
631 do_check_eq(1, c._cachedStatements.size);
632
633 yield c.executeCached("SELECT * from files");
634 do_check_eq(2, c._cachedStatements.size);
635
636 yield c.executeCached("SELECT * from dirs");
637 do_check_eq(2, c._cachedStatements.size);
638
639 c.discardCachedStatements();
640 do_check_eq(0, c._cachedStatements.size);
641
642 yield c.close();
643 });
644
645 add_task(function test_programmatic_binding() {
646 let c = yield getDummyDatabase("programmatic_binding");
647
648 let bindings = [
649 {id: 1, path: "foobar"},
650 {id: null, path: "baznoo"},
651 {id: 5, path: "toofoo"},
652 ];
653
654 let sql = "INSERT INTO dirs VALUES (:id, :path)";
655 let result = yield c.execute(sql, bindings);
656 do_check_eq(result.length, 0);
657
658 let rows = yield c.executeCached("SELECT * from dirs");
659 do_check_eq(rows.length, 3);
660 yield c.close();
661 });
662
663 add_task(function test_programmatic_binding_transaction() {
664 let c = yield getDummyDatabase("programmatic_binding_transaction");
665
666 let bindings = [
667 {id: 1, path: "foobar"},
668 {id: null, path: "baznoo"},
669 {id: 5, path: "toofoo"},
670 ];
671
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);
676
677 let rows = yield c.executeCached("SELECT * from dirs");
678 do_check_eq(rows.length, 3);
679 });
680
681 // Transaction committed.
682 let rows = yield c.executeCached("SELECT * from dirs");
683 do_check_eq(rows.length, 3);
684 yield c.close();
685 });
686
687 add_task(function test_programmatic_binding_transaction_partial_rollback() {
688 let c = yield getDummyDatabase("programmatic_binding_transaction_partial_rollback");
689
690 let bindings = [
691 {id: 2, path: "foobar"},
692 {id: 3, path: "toofoo"},
693 ];
694
695 let sql = "INSERT INTO dirs VALUES (:id, :path)";
696
697 // Add some data in an implicit transaction before beginning the batch insert.
698 yield c.execute(sql, {id: 1, path: "works"});
699
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]);
705
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 }
714
715 // We did not get to the end of our in-transaction block.
716 do_check_false(secondSucceeded);
717
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 });
725
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");
732
733 let bindings = [
734 {id: 2, path: "foobar"},
735 {id: 1, path: "toofoo"},
736 ];
737
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 }
747
748 do_check_false(secondSucceeded);
749
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 });
756
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);
765
766 let db = Services.storage.openDatabase(file);
767 print("Opened " + db);
768
769 db.executeSimpleSQL("CREATE TABLE types (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, UNIQUE (name))");
770 print("Executed setup.");
771
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);
780
781 print("Beginning transaction.");
782 let begin = db.createAsyncStatement("BEGIN DEFERRED TRANSACTION");
783 let end = db.createAsyncStatement("COMMIT TRANSACTION");
784
785 let deferred = Promise.defer();
786 begin.executeAsync({
787 handleCompletion: function (reason) {
788 deferred.resolve();
789 }
790 });
791 yield deferred.promise;
792
793 statement.bindParameters(params);
794
795 deferred = Promise.defer();
796 print("Executing async.");
797 statement.executeAsync({
798 handleResult: function (resultSet) {
799 },
800
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 },
808
809 handleCompletion: function (reason) {
810 print("Completed.");
811 deferred.resolve();
812 }
813 });
814
815 yield deferred.promise;
816
817 deferred = Promise.defer();
818 end.executeAsync({
819 handleCompletion: function (reason) {
820 deferred.resolve();
821 }
822 });
823 yield deferred.promise;
824
825 statement.finalize();
826 begin.finalize();
827 end.finalize();
828
829 deferred = Promise.defer();
830 db.asyncClose(function () {
831 deferred.resolve()
832 });
833 yield deferred.promise;
834 });
835
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 });
851
852 let clone = yield Sqlite.cloneStorageConnection({ connection: c, readOnly: true });
853 // Just check that it works.
854 yield clone.execute("SELECT 1");
855
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)");
859
860 // Closing order should not matter.
861 yield c.asyncClose();
862 yield clone2.close();
863 yield clone.close();
864 });
865
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 });
875
876 /**
877 * Test clone() method.
878 */
879 add_task(function* test_clone() {
880 let c = yield getDummyDatabase("clone");
881
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 });
889
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});
896
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