1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is Indexed Database.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * The Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Ben Turner <bent.mozilla@gmail.com>
23 : * Kyle Huey <me@kylehuey.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "OpenDatabaseHelper.h"
40 :
41 : #include "nsIFile.h"
42 :
43 : #include "mozilla/storage.h"
44 : #include "nsContentUtils.h"
45 : #include "nsEscape.h"
46 : #include "nsThreadUtils.h"
47 : #include "snappy/snappy.h"
48 : #include "test_quota.h"
49 :
50 : #include "IDBEvents.h"
51 : #include "IDBFactory.h"
52 : #include "IndexedDatabaseManager.h"
53 :
54 : using namespace mozilla;
55 : USING_INDEXEDDB_NAMESPACE
56 :
57 : namespace {
58 :
59 : // If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
60 : // schema version.
61 : PR_STATIC_ASSERT(JS_STRUCTURED_CLONE_VERSION == 1);
62 :
63 : // Major schema version. Bump for almost everything.
64 : const PRUint32 kMajorSchemaVersion = 12;
65 :
66 : // Minor schema version. Should almost always be 0 (maybe bump on release
67 : // branches if we have to).
68 : const PRUint32 kMinorSchemaVersion = 0;
69 :
70 : // The schema version we store in the SQLite database is a (signed) 32-bit
71 : // integer. The major version is left-shifted 4 bits so the max value is
72 : // 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
73 : PR_STATIC_ASSERT(kMajorSchemaVersion <= 0xFFFFFFF);
74 : PR_STATIC_ASSERT(kMajorSchemaVersion <= 0xF);
75 :
76 : inline
77 : PRInt32
78 0 : MakeSchemaVersion(PRUint32 aMajorSchemaVersion,
79 : PRUint32 aMinorSchemaVersion)
80 : {
81 0 : return PRInt32((aMajorSchemaVersion << 4) + aMinorSchemaVersion);
82 : }
83 :
84 : const PRInt32 kSQLiteSchemaVersion = PRInt32((kMajorSchemaVersion << 4) +
85 : kMinorSchemaVersion);
86 :
87 : inline
88 : PRUint32 GetMajorSchemaVersion(PRInt32 aSchemaVersion)
89 : {
90 : return PRUint32(aSchemaVersion) >> 4;
91 : }
92 :
93 : inline
94 : PRUint32 GetMinorSchemaVersion(PRInt32 aSchemaVersion)
95 : {
96 : return PRUint32(aSchemaVersion) & 0xF;
97 : }
98 :
99 : nsresult
100 76 : GetDatabaseFilename(const nsAString& aName,
101 : nsAString& aDatabaseFilename)
102 : {
103 76 : aDatabaseFilename.AppendInt(HashString(aName));
104 :
105 152 : nsCString escapedName;
106 76 : if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) {
107 0 : NS_WARNING("Can't escape database name!");
108 0 : return NS_ERROR_UNEXPECTED;
109 : }
110 :
111 76 : const char* forwardIter = escapedName.BeginReading();
112 76 : const char* backwardIter = escapedName.EndReading() - 1;
113 :
114 152 : nsCString substring;
115 1127 : while (forwardIter <= backwardIter && substring.Length() < 21) {
116 975 : if (substring.Length() % 2) {
117 450 : substring.Append(*backwardIter--);
118 : }
119 : else {
120 525 : substring.Append(*forwardIter++);
121 : }
122 : }
123 :
124 76 : aDatabaseFilename.Append(NS_ConvertASCIItoUTF16(substring));
125 :
126 76 : return NS_OK;
127 : }
128 :
129 : nsresult
130 52 : CreateFileTables(mozIStorageConnection* aDBConn)
131 : {
132 : // Table `file`
133 52 : nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
134 : "CREATE TABLE file ("
135 : "id INTEGER PRIMARY KEY, "
136 : "refcount INTEGER NOT NULL"
137 : ");"
138 52 : ));
139 52 : NS_ENSURE_SUCCESS(rv, rv);
140 :
141 52 : rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
142 : "CREATE TRIGGER object_data_insert_trigger "
143 : "AFTER INSERT ON object_data "
144 : "FOR EACH ROW "
145 : "WHEN NEW.file_ids IS NOT NULL "
146 : "BEGIN "
147 : "SELECT update_refcount(NULL, NEW.file_ids); "
148 : "END;"
149 52 : ));
150 52 : NS_ENSURE_SUCCESS(rv, rv);
151 :
152 52 : rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
153 : "CREATE TRIGGER object_data_update_trigger "
154 : "AFTER UPDATE OF file_ids ON object_data "
155 : "FOR EACH ROW "
156 : "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
157 : "BEGIN "
158 : "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
159 : "END;"
160 52 : ));
161 52 : NS_ENSURE_SUCCESS(rv, rv);
162 :
163 52 : rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
164 : "CREATE TRIGGER object_data_delete_trigger "
165 : "AFTER DELETE ON object_data "
166 : "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
167 : "BEGIN "
168 : "SELECT update_refcount(OLD.file_ids, NULL); "
169 : "END;"
170 52 : ));
171 52 : NS_ENSURE_SUCCESS(rv, rv);
172 :
173 52 : rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
174 : "CREATE TRIGGER file_update_trigger "
175 : "AFTER UPDATE ON file "
176 : "FOR EACH ROW WHEN NEW.refcount = 0 "
177 : "BEGIN "
178 : "DELETE FROM file WHERE id = OLD.id; "
179 : "END;"
180 52 : ));
181 52 : NS_ENSURE_SUCCESS(rv, rv);
182 :
183 52 : return NS_OK;
184 : }
185 :
186 : nsresult
187 52 : CreateTables(mozIStorageConnection* aDBConn)
188 : {
189 52 : NS_PRECONDITION(!NS_IsMainThread(),
190 : "Creating tables on the main thread!");
191 52 : NS_PRECONDITION(aDBConn, "Passing a null database connection!");
192 :
193 : // Table `database`
194 52 : nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
195 : "CREATE TABLE database ("
196 : "name TEXT NOT NULL, "
197 : "version INTEGER NOT NULL DEFAULT 0"
198 : ");"
199 52 : ));
200 52 : NS_ENSURE_SUCCESS(rv, rv);
201 :
202 : // Table `object_store`
203 52 : rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
204 : "CREATE TABLE object_store ("
205 : "id INTEGER PRIMARY KEY, "
206 : "auto_increment INTEGER NOT NULL DEFAULT 0, "
207 : "name TEXT NOT NULL, "
208 : "key_path TEXT, "
209 : "UNIQUE (name)"
210 : ");"
211 52 : ));
212 52 : NS_ENSURE_SUCCESS(rv, rv);
213 :
214 : // Table `object_data`
215 52 : rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
216 : "CREATE TABLE object_data ("
217 : "id INTEGER PRIMARY KEY, "
218 : "object_store_id INTEGER NOT NULL, "
219 : "key_value BLOB DEFAULT NULL, "
220 : "file_ids TEXT, "
221 : "data BLOB NOT NULL, "
222 : "UNIQUE (object_store_id, key_value), "
223 : "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
224 : "CASCADE"
225 : ");"
226 52 : ));
227 52 : NS_ENSURE_SUCCESS(rv, rv);
228 :
229 : // Table `index`
230 52 : rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
231 : "CREATE TABLE object_store_index ("
232 : "id INTEGER PRIMARY KEY, "
233 : "object_store_id INTEGER NOT NULL, "
234 : "name TEXT NOT NULL, "
235 : "key_path TEXT NOT NULL, "
236 : "unique_index INTEGER NOT NULL, "
237 : "multientry INTEGER NOT NULL, "
238 : "UNIQUE (object_store_id, name), "
239 : "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
240 : "CASCADE"
241 : ");"
242 52 : ));
243 52 : NS_ENSURE_SUCCESS(rv, rv);
244 :
245 : // Table `index_data`
246 52 : rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
247 : "CREATE TABLE index_data ("
248 : "index_id INTEGER NOT NULL, "
249 : "value BLOB NOT NULL, "
250 : "object_data_key BLOB NOT NULL, "
251 : "object_data_id INTEGER NOT NULL, "
252 : "PRIMARY KEY (index_id, value, object_data_key), "
253 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
254 : "CASCADE, "
255 : "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
256 : "CASCADE"
257 : ");"
258 52 : ));
259 52 : NS_ENSURE_SUCCESS(rv, rv);
260 :
261 : // Need this to make cascading deletes from object_data and object_store fast.
262 52 : rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
263 : "CREATE INDEX index_data_object_data_id_index "
264 : "ON index_data (object_data_id);"
265 52 : ));
266 52 : NS_ENSURE_SUCCESS(rv, rv);
267 :
268 : // Table `unique_index_data`
269 52 : rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
270 : "CREATE TABLE unique_index_data ("
271 : "index_id INTEGER NOT NULL, "
272 : "value BLOB NOT NULL, "
273 : "object_data_key BLOB NOT NULL, "
274 : "object_data_id INTEGER NOT NULL, "
275 : "PRIMARY KEY (index_id, value, object_data_key), "
276 : "UNIQUE (index_id, value), "
277 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
278 : "CASCADE "
279 : "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
280 : "CASCADE"
281 : ");"
282 52 : ));
283 52 : NS_ENSURE_SUCCESS(rv, rv);
284 :
285 : // Need this to make cascading deletes from object_data and object_store fast.
286 52 : rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
287 : "CREATE INDEX unique_index_data_object_data_id_index "
288 : "ON unique_index_data (object_data_id);"
289 52 : ));
290 52 : NS_ENSURE_SUCCESS(rv, rv);
291 :
292 52 : rv = CreateFileTables(aDBConn);
293 52 : NS_ENSURE_SUCCESS(rv, rv);
294 :
295 52 : rv = aDBConn->SetSchemaVersion(kSQLiteSchemaVersion);
296 52 : NS_ENSURE_SUCCESS(rv, rv);
297 :
298 52 : return NS_OK;
299 : }
300 :
301 : nsresult
302 0 : UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection)
303 : {
304 : nsresult rv;
305 :
306 : // All we changed is the type of the version column, so lets try to
307 : // convert that to an integer, and if we fail, set it to 0.
308 0 : nsCOMPtr<mozIStorageStatement> stmt;
309 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
310 : "SELECT name, version, dataVersion "
311 : "FROM database"
312 0 : ), getter_AddRefs(stmt));
313 0 : NS_ENSURE_SUCCESS(rv, rv);
314 :
315 0 : nsString name;
316 : PRInt32 intVersion;
317 : PRInt64 dataVersion;
318 :
319 : {
320 0 : mozStorageStatementScoper scoper(stmt);
321 :
322 : bool hasResults;
323 0 : rv = stmt->ExecuteStep(&hasResults);
324 0 : NS_ENSURE_SUCCESS(rv, rv);
325 0 : NS_ENSURE_TRUE(hasResults, NS_ERROR_FAILURE);
326 :
327 0 : nsString version;
328 0 : rv = stmt->GetString(1, version);
329 0 : NS_ENSURE_SUCCESS(rv, rv);
330 :
331 0 : intVersion = version.ToInteger(&rv, 10);
332 0 : if (NS_FAILED(rv)) {
333 0 : intVersion = 0;
334 : }
335 :
336 0 : rv = stmt->GetString(0, name);
337 0 : NS_ENSURE_SUCCESS(rv, rv);
338 :
339 0 : rv = stmt->GetInt64(2, &dataVersion);
340 0 : NS_ENSURE_SUCCESS(rv, rv);
341 : }
342 :
343 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
344 : "DROP TABLE database"
345 0 : ));
346 0 : NS_ENSURE_SUCCESS(rv, rv);
347 :
348 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
349 : "CREATE TABLE database ("
350 : "name TEXT NOT NULL, "
351 : "version INTEGER NOT NULL DEFAULT 0, "
352 : "dataVersion INTEGER NOT NULL"
353 : ");"
354 0 : ));
355 0 : NS_ENSURE_SUCCESS(rv, rv);
356 :
357 0 : rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
358 : "INSERT INTO database (name, version, dataVersion) "
359 : "VALUES (:name, :version, :dataVersion)"
360 0 : ), getter_AddRefs(stmt));
361 0 : NS_ENSURE_SUCCESS(rv, rv);
362 :
363 : {
364 0 : mozStorageStatementScoper scoper(stmt);
365 :
366 0 : rv = stmt->BindStringParameter(0, name);
367 0 : NS_ENSURE_SUCCESS(rv, rv);
368 :
369 0 : rv = stmt->BindInt32Parameter(1, intVersion);
370 0 : NS_ENSURE_SUCCESS(rv, rv);
371 :
372 0 : rv = stmt->BindInt64Parameter(2, dataVersion);
373 0 : NS_ENSURE_SUCCESS(rv, rv);
374 :
375 0 : rv = stmt->Execute();
376 0 : NS_ENSURE_SUCCESS(rv, rv);
377 : }
378 :
379 0 : rv = aConnection->SetSchemaVersion(5);
380 0 : NS_ENSURE_SUCCESS(rv, rv);
381 :
382 0 : return NS_OK;
383 : }
384 :
385 : nsresult
386 0 : UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection)
387 : {
388 : // First, drop all the indexes we're no longer going to use.
389 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
390 : "DROP INDEX key_index;"
391 0 : ));
392 0 : NS_ENSURE_SUCCESS(rv, rv);
393 :
394 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
395 : "DROP INDEX ai_key_index;"
396 0 : ));
397 0 : NS_ENSURE_SUCCESS(rv, rv);
398 :
399 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
400 : "DROP INDEX value_index;"
401 0 : ));
402 0 : NS_ENSURE_SUCCESS(rv, rv);
403 :
404 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
405 : "DROP INDEX ai_value_index;"
406 0 : ));
407 0 : NS_ENSURE_SUCCESS(rv, rv);
408 :
409 : // Now, reorder the columns of object_data to put the blob data last. We do
410 : // this by copying into a temporary table, dropping the original, then copying
411 : // back into a newly created table.
412 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
413 : "CREATE TEMPORARY TABLE temp_upgrade ("
414 : "id INTEGER PRIMARY KEY, "
415 : "object_store_id, "
416 : "key_value, "
417 : "data "
418 : ");"
419 0 : ));
420 0 : NS_ENSURE_SUCCESS(rv, rv);
421 :
422 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
423 : "INSERT INTO temp_upgrade "
424 : "SELECT id, object_store_id, key_value, data "
425 : "FROM object_data;"
426 0 : ));
427 0 : NS_ENSURE_SUCCESS(rv, rv);
428 :
429 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
430 : "DROP TABLE object_data;"
431 0 : ));
432 0 : NS_ENSURE_SUCCESS(rv, rv);
433 :
434 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
435 : "CREATE TABLE object_data ("
436 : "id INTEGER PRIMARY KEY, "
437 : "object_store_id INTEGER NOT NULL, "
438 : "key_value DEFAULT NULL, "
439 : "data BLOB NOT NULL, "
440 : "UNIQUE (object_store_id, key_value), "
441 : "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
442 : "CASCADE"
443 : ");"
444 0 : ));
445 0 : NS_ENSURE_SUCCESS(rv, rv);
446 :
447 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
448 : "INSERT INTO object_data "
449 : "SELECT id, object_store_id, key_value, data "
450 : "FROM temp_upgrade;"
451 0 : ));
452 0 : NS_ENSURE_SUCCESS(rv, rv);
453 :
454 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
455 : "DROP TABLE temp_upgrade;"
456 0 : ));
457 0 : NS_ENSURE_SUCCESS(rv, rv);
458 :
459 : // We need to add a unique constraint to our ai_object_data table. Copy all
460 : // the data out of it using a temporary table as before.
461 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
462 : "CREATE TEMPORARY TABLE temp_upgrade ("
463 : "id INTEGER PRIMARY KEY, "
464 : "object_store_id, "
465 : "data "
466 : ");"
467 0 : ));
468 0 : NS_ENSURE_SUCCESS(rv, rv);
469 :
470 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
471 : "INSERT INTO temp_upgrade "
472 : "SELECT id, object_store_id, data "
473 : "FROM ai_object_data;"
474 0 : ));
475 0 : NS_ENSURE_SUCCESS(rv, rv);
476 :
477 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
478 : "DROP TABLE ai_object_data;"
479 0 : ));
480 0 : NS_ENSURE_SUCCESS(rv, rv);
481 :
482 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
483 : "CREATE TABLE ai_object_data ("
484 : "id INTEGER PRIMARY KEY AUTOINCREMENT, "
485 : "object_store_id INTEGER NOT NULL, "
486 : "data BLOB NOT NULL, "
487 : "UNIQUE (object_store_id, id), "
488 : "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
489 : "CASCADE"
490 : ");"
491 0 : ));
492 0 : NS_ENSURE_SUCCESS(rv, rv);
493 :
494 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
495 : "INSERT INTO ai_object_data "
496 : "SELECT id, object_store_id, data "
497 : "FROM temp_upgrade;"
498 0 : ));
499 0 : NS_ENSURE_SUCCESS(rv, rv);
500 :
501 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
502 : "DROP TABLE temp_upgrade;"
503 0 : ));
504 0 : NS_ENSURE_SUCCESS(rv, rv);
505 :
506 : // Fix up the index_data table. We're reordering the columns as well as
507 : // changing the primary key from being a simple id to being a composite.
508 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
509 : "CREATE TEMPORARY TABLE temp_upgrade ("
510 : "index_id, "
511 : "value, "
512 : "object_data_key, "
513 : "object_data_id "
514 : ");"
515 0 : ));
516 0 : NS_ENSURE_SUCCESS(rv, rv);
517 :
518 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
519 : "INSERT INTO temp_upgrade "
520 : "SELECT index_id, value, object_data_key, object_data_id "
521 : "FROM index_data;"
522 0 : ));
523 0 : NS_ENSURE_SUCCESS(rv, rv);
524 :
525 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
526 : "DROP TABLE index_data;"
527 0 : ));
528 0 : NS_ENSURE_SUCCESS(rv, rv);
529 :
530 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
531 : "CREATE TABLE index_data ("
532 : "index_id INTEGER NOT NULL, "
533 : "value NOT NULL, "
534 : "object_data_key NOT NULL, "
535 : "object_data_id INTEGER NOT NULL, "
536 : "PRIMARY KEY (index_id, value, object_data_key), "
537 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
538 : "CASCADE, "
539 : "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
540 : "CASCADE"
541 : ");"
542 0 : ));
543 0 : NS_ENSURE_SUCCESS(rv, rv);
544 :
545 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
546 : "INSERT OR IGNORE INTO index_data "
547 : "SELECT index_id, value, object_data_key, object_data_id "
548 : "FROM temp_upgrade;"
549 0 : ));
550 0 : NS_ENSURE_SUCCESS(rv, rv);
551 :
552 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
553 : "DROP TABLE temp_upgrade;"
554 0 : ));
555 0 : NS_ENSURE_SUCCESS(rv, rv);
556 :
557 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
558 : "CREATE INDEX index_data_object_data_id_index "
559 : "ON index_data (object_data_id);"
560 0 : ));
561 0 : NS_ENSURE_SUCCESS(rv, rv);
562 :
563 : // Fix up the unique_index_data table. We're reordering the columns as well as
564 : // changing the primary key from being a simple id to being a composite.
565 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
566 : "CREATE TEMPORARY TABLE temp_upgrade ("
567 : "index_id, "
568 : "value, "
569 : "object_data_key, "
570 : "object_data_id "
571 : ");"
572 0 : ));
573 0 : NS_ENSURE_SUCCESS(rv, rv);
574 :
575 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
576 : "INSERT INTO temp_upgrade "
577 : "SELECT index_id, value, object_data_key, object_data_id "
578 : "FROM unique_index_data;"
579 0 : ));
580 0 : NS_ENSURE_SUCCESS(rv, rv);
581 :
582 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
583 : "DROP TABLE unique_index_data;"
584 0 : ));
585 0 : NS_ENSURE_SUCCESS(rv, rv);
586 :
587 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
588 : "CREATE TABLE unique_index_data ("
589 : "index_id INTEGER NOT NULL, "
590 : "value NOT NULL, "
591 : "object_data_key NOT NULL, "
592 : "object_data_id INTEGER NOT NULL, "
593 : "PRIMARY KEY (index_id, value, object_data_key), "
594 : "UNIQUE (index_id, value), "
595 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
596 : "CASCADE "
597 : "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
598 : "CASCADE"
599 : ");"
600 0 : ));
601 0 : NS_ENSURE_SUCCESS(rv, rv);
602 :
603 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
604 : "INSERT INTO unique_index_data "
605 : "SELECT index_id, value, object_data_key, object_data_id "
606 : "FROM temp_upgrade;"
607 0 : ));
608 0 : NS_ENSURE_SUCCESS(rv, rv);
609 :
610 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
611 : "DROP TABLE temp_upgrade;"
612 0 : ));
613 0 : NS_ENSURE_SUCCESS(rv, rv);
614 :
615 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
616 : "CREATE INDEX unique_index_data_object_data_id_index "
617 : "ON unique_index_data (object_data_id);"
618 0 : ));
619 0 : NS_ENSURE_SUCCESS(rv, rv);
620 :
621 : // Fix up the ai_index_data table. We're reordering the columns as well as
622 : // changing the primary key from being a simple id to being a composite.
623 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
624 : "CREATE TEMPORARY TABLE temp_upgrade ("
625 : "index_id, "
626 : "value, "
627 : "ai_object_data_id "
628 : ");"
629 0 : ));
630 0 : NS_ENSURE_SUCCESS(rv, rv);
631 :
632 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
633 : "INSERT INTO temp_upgrade "
634 : "SELECT index_id, value, ai_object_data_id "
635 : "FROM ai_index_data;"
636 0 : ));
637 0 : NS_ENSURE_SUCCESS(rv, rv);
638 :
639 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
640 : "DROP TABLE ai_index_data;"
641 0 : ));
642 0 : NS_ENSURE_SUCCESS(rv, rv);
643 :
644 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
645 : "CREATE TABLE ai_index_data ("
646 : "index_id INTEGER NOT NULL, "
647 : "value NOT NULL, "
648 : "ai_object_data_id INTEGER NOT NULL, "
649 : "PRIMARY KEY (index_id, value, ai_object_data_id), "
650 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
651 : "CASCADE, "
652 : "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
653 : "CASCADE"
654 : ");"
655 0 : ));
656 0 : NS_ENSURE_SUCCESS(rv, rv);
657 :
658 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
659 : "INSERT OR IGNORE INTO ai_index_data "
660 : "SELECT index_id, value, ai_object_data_id "
661 : "FROM temp_upgrade;"
662 0 : ));
663 0 : NS_ENSURE_SUCCESS(rv, rv);
664 :
665 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
666 : "DROP TABLE temp_upgrade;"
667 0 : ));
668 0 : NS_ENSURE_SUCCESS(rv, rv);
669 :
670 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
671 : "CREATE INDEX ai_index_data_ai_object_data_id_index "
672 : "ON ai_index_data (ai_object_data_id);"
673 0 : ));
674 0 : NS_ENSURE_SUCCESS(rv, rv);
675 :
676 : // Fix up the ai_unique_index_data table. We're reordering the columns as well
677 : // as changing the primary key from being a simple id to being a composite.
678 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
679 : "CREATE TEMPORARY TABLE temp_upgrade ("
680 : "index_id, "
681 : "value, "
682 : "ai_object_data_id "
683 : ");"
684 0 : ));
685 0 : NS_ENSURE_SUCCESS(rv, rv);
686 :
687 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
688 : "INSERT INTO temp_upgrade "
689 : "SELECT index_id, value, ai_object_data_id "
690 : "FROM ai_unique_index_data;"
691 0 : ));
692 0 : NS_ENSURE_SUCCESS(rv, rv);
693 :
694 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
695 : "DROP TABLE ai_unique_index_data;"
696 0 : ));
697 0 : NS_ENSURE_SUCCESS(rv, rv);
698 :
699 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
700 : "CREATE TABLE ai_unique_index_data ("
701 : "index_id INTEGER NOT NULL, "
702 : "value NOT NULL, "
703 : "ai_object_data_id INTEGER NOT NULL, "
704 : "UNIQUE (index_id, value), "
705 : "PRIMARY KEY (index_id, value, ai_object_data_id), "
706 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
707 : "CASCADE, "
708 : "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
709 : "CASCADE"
710 : ");"
711 0 : ));
712 0 : NS_ENSURE_SUCCESS(rv, rv);
713 :
714 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
715 : "INSERT INTO ai_unique_index_data "
716 : "SELECT index_id, value, ai_object_data_id "
717 : "FROM temp_upgrade;"
718 0 : ));
719 0 : NS_ENSURE_SUCCESS(rv, rv);
720 :
721 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
722 : "DROP TABLE temp_upgrade;"
723 0 : ));
724 0 : NS_ENSURE_SUCCESS(rv, rv);
725 :
726 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
727 : "CREATE INDEX ai_unique_index_data_ai_object_data_id_index "
728 : "ON ai_unique_index_data (ai_object_data_id);"
729 0 : ));
730 0 : NS_ENSURE_SUCCESS(rv, rv);
731 :
732 0 : rv = aConnection->SetSchemaVersion(6);
733 0 : NS_ENSURE_SUCCESS(rv, rv);
734 :
735 0 : return NS_OK;
736 : }
737 :
738 : nsresult
739 0 : UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection)
740 : {
741 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
742 : "CREATE TEMPORARY TABLE temp_upgrade ("
743 : "id, "
744 : "name, "
745 : "key_path, "
746 : "auto_increment"
747 : ");"
748 0 : ));
749 0 : NS_ENSURE_SUCCESS(rv, rv);
750 :
751 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
752 : "INSERT INTO temp_upgrade "
753 : "SELECT id, name, key_path, auto_increment "
754 : "FROM object_store;"
755 0 : ));
756 0 : NS_ENSURE_SUCCESS(rv, rv);
757 :
758 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
759 : "DROP TABLE object_store;"
760 0 : ));
761 0 : NS_ENSURE_SUCCESS(rv, rv);
762 :
763 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
764 : "CREATE TABLE object_store ("
765 : "id INTEGER PRIMARY KEY, "
766 : "auto_increment INTEGER NOT NULL DEFAULT 0, "
767 : "name TEXT NOT NULL, "
768 : "key_path TEXT, "
769 : "UNIQUE (name)"
770 : ");"
771 0 : ));
772 0 : NS_ENSURE_SUCCESS(rv, rv);
773 :
774 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
775 : "INSERT INTO object_store "
776 : "SELECT id, auto_increment, name, nullif(key_path, '') "
777 : "FROM temp_upgrade;"
778 0 : ));
779 0 : NS_ENSURE_SUCCESS(rv, rv);
780 :
781 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
782 : "DROP TABLE temp_upgrade;"
783 0 : ));
784 0 : NS_ENSURE_SUCCESS(rv, rv);
785 :
786 0 : rv = aConnection->SetSchemaVersion(7);
787 0 : NS_ENSURE_SUCCESS(rv, rv);
788 :
789 0 : return NS_OK;
790 : }
791 :
792 : nsresult
793 0 : UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection)
794 : {
795 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
796 : "CREATE TEMPORARY TABLE temp_upgrade ("
797 : "id, "
798 : "object_store_id, "
799 : "name, "
800 : "key_path, "
801 : "unique_index, "
802 : "object_store_autoincrement"
803 : ");"
804 0 : ));
805 0 : NS_ENSURE_SUCCESS(rv, rv);
806 :
807 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
808 : "INSERT INTO temp_upgrade "
809 : "SELECT id, object_store_id, name, key_path, "
810 : "unique_index, object_store_autoincrement "
811 : "FROM object_store_index;"
812 0 : ));
813 0 : NS_ENSURE_SUCCESS(rv, rv);
814 :
815 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
816 : "DROP TABLE object_store_index;"
817 0 : ));
818 0 : NS_ENSURE_SUCCESS(rv, rv);
819 :
820 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
821 : "CREATE TABLE object_store_index ("
822 : "id INTEGER, "
823 : "object_store_id INTEGER NOT NULL, "
824 : "name TEXT NOT NULL, "
825 : "key_path TEXT NOT NULL, "
826 : "unique_index INTEGER NOT NULL, "
827 : "multientry INTEGER NOT NULL, "
828 : "object_store_autoincrement INTERGER NOT NULL, "
829 : "PRIMARY KEY (id), "
830 : "UNIQUE (object_store_id, name), "
831 : "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
832 : "CASCADE"
833 : ");"
834 0 : ));
835 0 : NS_ENSURE_SUCCESS(rv, rv);
836 :
837 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
838 : "INSERT INTO object_store_index "
839 : "SELECT id, object_store_id, name, key_path, "
840 : "unique_index, 0, object_store_autoincrement "
841 : "FROM temp_upgrade;"
842 0 : ));
843 0 : NS_ENSURE_SUCCESS(rv, rv);
844 :
845 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
846 : "DROP TABLE temp_upgrade;"
847 0 : ));
848 0 : NS_ENSURE_SUCCESS(rv, rv);
849 :
850 0 : rv = aConnection->SetSchemaVersion(8);
851 0 : NS_ENSURE_SUCCESS(rv, rv);
852 :
853 0 : return NS_OK;
854 : }
855 :
856 : class CompressDataBlobsFunction : public mozIStorageFunction
857 0 : {
858 : public:
859 : NS_DECL_ISUPPORTS
860 :
861 : NS_IMETHOD
862 0 : OnFunctionCall(mozIStorageValueArray* aArguments,
863 : nsIVariant** aResult)
864 : {
865 : PRUint32 argc;
866 0 : nsresult rv = aArguments->GetNumEntries(&argc);
867 0 : NS_ENSURE_SUCCESS(rv, rv);
868 :
869 0 : if (argc != 1) {
870 0 : NS_WARNING("Don't call me with the wrong number of arguments!");
871 0 : return NS_ERROR_UNEXPECTED;
872 : }
873 :
874 : PRInt32 type;
875 0 : rv = aArguments->GetTypeOfIndex(0, &type);
876 0 : NS_ENSURE_SUCCESS(rv, rv);
877 :
878 0 : if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
879 0 : NS_WARNING("Don't call me with the wrong type of arguments!");
880 0 : return NS_ERROR_UNEXPECTED;
881 : }
882 :
883 : const PRUint8* uncompressed;
884 : PRUint32 uncompressedLength;
885 0 : rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed);
886 0 : NS_ENSURE_SUCCESS(rv, rv);
887 :
888 0 : size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
889 0 : nsAutoArrayPtr<char> compressed(new char[compressedLength]);
890 :
891 : snappy::RawCompress(reinterpret_cast<const char*>(uncompressed),
892 : uncompressedLength, compressed.get(),
893 0 : &compressedLength);
894 :
895 0 : std::pair<const void *, int> data(static_cast<void*>(compressed.get()),
896 0 : int(compressedLength));
897 :
898 : // XXX This copies the buffer again... There doesn't appear to be any way to
899 : // preallocate space and write directly to a BlobVariant at the moment.
900 0 : nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
901 :
902 0 : result.forget(aResult);
903 0 : return NS_OK;
904 : }
905 : };
906 :
907 0 : NS_IMPL_ISUPPORTS1(CompressDataBlobsFunction, mozIStorageFunction)
908 :
909 : nsresult
910 0 : UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection)
911 : {
912 : // We no longer use the dataVersion column.
913 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
914 : "UPDATE database SET dataVersion = 0;"
915 0 : ));
916 0 : NS_ENSURE_SUCCESS(rv, rv);
917 :
918 0 : nsCOMPtr<mozIStorageFunction> compressor = new CompressDataBlobsFunction();
919 :
920 0 : NS_NAMED_LITERAL_CSTRING(compressorName, "compress");
921 :
922 0 : rv = aConnection->CreateFunction(compressorName, 1, compressor);
923 0 : NS_ENSURE_SUCCESS(rv, rv);
924 :
925 : // Turn off foreign key constraints before we do anything here.
926 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
927 : "UPDATE object_data SET data = compress(data);"
928 0 : ));
929 0 : NS_ENSURE_SUCCESS(rv, rv);
930 :
931 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
932 : "UPDATE ai_object_data SET data = compress(data);"
933 0 : ));
934 0 : NS_ENSURE_SUCCESS(rv, rv);
935 :
936 0 : rv = aConnection->RemoveFunction(compressorName);
937 0 : NS_ENSURE_SUCCESS(rv, rv);
938 :
939 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0));
940 0 : NS_ENSURE_SUCCESS(rv, rv);
941 :
942 0 : return NS_OK;
943 : }
944 :
945 : nsresult
946 0 : UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection)
947 : {
948 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
949 : "ALTER TABLE object_data ADD COLUMN file_ids TEXT;"
950 0 : ));
951 0 : NS_ENSURE_SUCCESS(rv, rv);
952 :
953 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
954 : "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;"
955 0 : ));
956 0 : NS_ENSURE_SUCCESS(rv, rv);
957 :
958 0 : rv = CreateFileTables(aConnection);
959 0 : NS_ENSURE_SUCCESS(rv, rv);
960 :
961 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0));
962 0 : NS_ENSURE_SUCCESS(rv, rv);
963 :
964 0 : return NS_OK;
965 : }
966 :
967 : nsresult
968 0 : UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection)
969 : {
970 0 : nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
971 : "CREATE TEMPORARY TABLE temp_upgrade ("
972 : "id, "
973 : "object_store_id, "
974 : "name, "
975 : "key_path, "
976 : "unique_index, "
977 : "multientry"
978 : ");"
979 0 : ));
980 0 : NS_ENSURE_SUCCESS(rv, rv);
981 :
982 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
983 : "INSERT INTO temp_upgrade "
984 : "SELECT id, object_store_id, name, key_path, "
985 : "unique_index, multientry "
986 : "FROM object_store_index;"
987 0 : ));
988 0 : NS_ENSURE_SUCCESS(rv, rv);
989 :
990 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
991 : "DROP TABLE object_store_index;"
992 0 : ));
993 0 : NS_ENSURE_SUCCESS(rv, rv);
994 :
995 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
996 : "CREATE TABLE object_store_index ("
997 : "id INTEGER PRIMARY KEY, "
998 : "object_store_id INTEGER NOT NULL, "
999 : "name TEXT NOT NULL, "
1000 : "key_path TEXT NOT NULL, "
1001 : "unique_index INTEGER NOT NULL, "
1002 : "multientry INTEGER NOT NULL, "
1003 : "UNIQUE (object_store_id, name), "
1004 : "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1005 : "CASCADE"
1006 : ");"
1007 0 : ));
1008 0 : NS_ENSURE_SUCCESS(rv, rv);
1009 :
1010 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1011 : "INSERT INTO object_store_index "
1012 : "SELECT id, object_store_id, name, key_path, "
1013 : "unique_index, multientry "
1014 : "FROM temp_upgrade;"
1015 0 : ));
1016 0 : NS_ENSURE_SUCCESS(rv, rv);
1017 :
1018 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1019 : "DROP TABLE temp_upgrade;"
1020 0 : ));
1021 0 : NS_ENSURE_SUCCESS(rv, rv);
1022 :
1023 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1024 : "DROP TRIGGER object_data_insert_trigger;"
1025 0 : ));
1026 0 : NS_ENSURE_SUCCESS(rv, rv);
1027 :
1028 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1029 : "INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
1030 : "SELECT object_store_id, id, data, file_ids "
1031 : "FROM ai_object_data;"
1032 0 : ));
1033 0 : NS_ENSURE_SUCCESS(rv, rv);
1034 :
1035 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1036 : "CREATE TRIGGER object_data_insert_trigger "
1037 : "AFTER INSERT ON object_data "
1038 : "FOR EACH ROW "
1039 : "WHEN NEW.file_ids IS NOT NULL "
1040 : "BEGIN "
1041 : "SELECT update_refcount(NULL, NEW.file_ids); "
1042 : "END;"
1043 0 : ));
1044 0 : NS_ENSURE_SUCCESS(rv, rv);
1045 :
1046 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1047 : "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) "
1048 : "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id "
1049 : "FROM ai_index_data "
1050 : "INNER JOIN object_store_index ON "
1051 : "object_store_index.id = ai_index_data.index_id "
1052 : "INNER JOIN object_data ON "
1053 : "object_data.object_store_id = object_store_index.object_store_id AND "
1054 : "object_data.key_value = ai_index_data.ai_object_data_id;"
1055 0 : ));
1056 0 : NS_ENSURE_SUCCESS(rv, rv);
1057 :
1058 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1059 : "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) "
1060 : "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id "
1061 : "FROM ai_unique_index_data "
1062 : "INNER JOIN object_store_index ON "
1063 : "object_store_index.id = ai_unique_index_data.index_id "
1064 : "INNER JOIN object_data ON "
1065 : "object_data.object_store_id = object_store_index.object_store_id AND "
1066 : "object_data.key_value = ai_unique_index_data.ai_object_data_id;"
1067 0 : ));
1068 0 : NS_ENSURE_SUCCESS(rv, rv);
1069 :
1070 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1071 : "UPDATE object_store "
1072 : "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 "
1073 : "WHERE auto_increment;"
1074 0 : ));
1075 0 : NS_ENSURE_SUCCESS(rv, rv);
1076 :
1077 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1078 : "DROP TABLE ai_unique_index_data;"
1079 0 : ));
1080 0 : NS_ENSURE_SUCCESS(rv, rv);
1081 :
1082 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1083 : "DROP TABLE ai_index_data;"
1084 0 : ));
1085 0 : NS_ENSURE_SUCCESS(rv, rv);
1086 :
1087 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1088 : "DROP TABLE ai_object_data;"
1089 0 : ));
1090 0 : NS_ENSURE_SUCCESS(rv, rv);
1091 :
1092 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0));
1093 0 : NS_ENSURE_SUCCESS(rv, rv);
1094 :
1095 0 : return NS_OK;
1096 : }
1097 :
1098 : class EncodeKeysFunction : public mozIStorageFunction
1099 0 : {
1100 : public:
1101 : NS_DECL_ISUPPORTS
1102 :
1103 : NS_IMETHOD
1104 0 : OnFunctionCall(mozIStorageValueArray* aArguments,
1105 : nsIVariant** aResult)
1106 : {
1107 : PRUint32 argc;
1108 0 : nsresult rv = aArguments->GetNumEntries(&argc);
1109 0 : NS_ENSURE_SUCCESS(rv, rv);
1110 :
1111 0 : if (argc != 1) {
1112 0 : NS_WARNING("Don't call me with the wrong number of arguments!");
1113 0 : return NS_ERROR_UNEXPECTED;
1114 : }
1115 :
1116 : PRInt32 type;
1117 0 : rv = aArguments->GetTypeOfIndex(0, &type);
1118 0 : NS_ENSURE_SUCCESS(rv, rv);
1119 :
1120 0 : Key key;
1121 0 : if (type == mozIStorageStatement::VALUE_TYPE_INTEGER) {
1122 : PRInt64 intKey;
1123 0 : aArguments->GetInt64(0, &intKey);
1124 0 : key.SetFromInteger(intKey);
1125 : }
1126 0 : else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) {
1127 0 : nsString stringKey;
1128 0 : aArguments->GetString(0, stringKey);
1129 0 : key.SetFromString(stringKey);
1130 : }
1131 : else {
1132 0 : NS_WARNING("Don't call me with the wrong type of arguments!");
1133 0 : return NS_ERROR_UNEXPECTED;
1134 : }
1135 :
1136 0 : const nsCString& buffer = key.GetBuffer();
1137 :
1138 0 : std::pair<const void *, int> data(static_cast<const void*>(buffer.get()),
1139 0 : int(buffer.Length()));
1140 :
1141 0 : nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
1142 :
1143 0 : result.forget(aResult);
1144 0 : return NS_OK;
1145 : }
1146 : };
1147 :
1148 0 : NS_IMPL_ISUPPORTS1(EncodeKeysFunction, mozIStorageFunction)
1149 :
1150 : nsresult
1151 0 : UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection)
1152 : {
1153 0 : NS_NAMED_LITERAL_CSTRING(encoderName, "encode");
1154 :
1155 0 : nsCOMPtr<mozIStorageFunction> encoder = new EncodeKeysFunction();
1156 :
1157 0 : nsresult rv = aConnection->CreateFunction(encoderName, 1, encoder);
1158 0 : NS_ENSURE_SUCCESS(rv, rv);
1159 :
1160 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1161 : "CREATE TEMPORARY TABLE temp_upgrade ("
1162 : "id INTEGER PRIMARY KEY, "
1163 : "object_store_id, "
1164 : "key_value, "
1165 : "data, "
1166 : "file_ids "
1167 : ");"
1168 0 : ));
1169 0 : NS_ENSURE_SUCCESS(rv, rv);
1170 :
1171 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1172 : "INSERT INTO temp_upgrade "
1173 : "SELECT id, object_store_id, encode(key_value), data, file_ids "
1174 : "FROM object_data;"
1175 0 : ));
1176 0 : NS_ENSURE_SUCCESS(rv, rv);
1177 :
1178 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1179 : "DROP TABLE object_data;"
1180 0 : ));
1181 0 : NS_ENSURE_SUCCESS(rv, rv);
1182 :
1183 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1184 : "CREATE TABLE object_data ("
1185 : "id INTEGER PRIMARY KEY, "
1186 : "object_store_id INTEGER NOT NULL, "
1187 : "key_value BLOB DEFAULT NULL, "
1188 : "file_ids TEXT, "
1189 : "data BLOB NOT NULL, "
1190 : "UNIQUE (object_store_id, key_value), "
1191 : "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1192 : "CASCADE"
1193 : ");"
1194 0 : ));
1195 0 : NS_ENSURE_SUCCESS(rv, rv);
1196 :
1197 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1198 : "INSERT INTO object_data "
1199 : "SELECT id, object_store_id, key_value, file_ids, data "
1200 : "FROM temp_upgrade;"
1201 0 : ));
1202 0 : NS_ENSURE_SUCCESS(rv, rv);
1203 :
1204 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1205 : "DROP TABLE temp_upgrade;"
1206 0 : ));
1207 0 : NS_ENSURE_SUCCESS(rv, rv);
1208 :
1209 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1210 : "CREATE TRIGGER object_data_insert_trigger "
1211 : "AFTER INSERT ON object_data "
1212 : "FOR EACH ROW "
1213 : "WHEN NEW.file_ids IS NOT NULL "
1214 : "BEGIN "
1215 : "SELECT update_refcount(NULL, NEW.file_ids); "
1216 : "END;"
1217 0 : ));
1218 0 : NS_ENSURE_SUCCESS(rv, rv);
1219 :
1220 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1221 : "CREATE TRIGGER object_data_update_trigger "
1222 : "AFTER UPDATE OF file_ids ON object_data "
1223 : "FOR EACH ROW "
1224 : "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
1225 : "BEGIN "
1226 : "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
1227 : "END;"
1228 0 : ));
1229 0 : NS_ENSURE_SUCCESS(rv, rv);
1230 :
1231 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1232 : "CREATE TRIGGER object_data_delete_trigger "
1233 : "AFTER DELETE ON object_data "
1234 : "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
1235 : "BEGIN "
1236 : "SELECT update_refcount(OLD.file_ids, NULL); "
1237 : "END;"
1238 0 : ));
1239 0 : NS_ENSURE_SUCCESS(rv, rv);
1240 :
1241 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1242 : "CREATE TEMPORARY TABLE temp_upgrade ("
1243 : "index_id, "
1244 : "value, "
1245 : "object_data_key, "
1246 : "object_data_id "
1247 : ");"
1248 0 : ));
1249 0 : NS_ENSURE_SUCCESS(rv, rv);
1250 :
1251 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1252 : "INSERT INTO temp_upgrade "
1253 : "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
1254 : "FROM index_data;"
1255 0 : ));
1256 0 : NS_ENSURE_SUCCESS(rv, rv);
1257 :
1258 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1259 : "DROP TABLE index_data;"
1260 0 : ));
1261 0 : NS_ENSURE_SUCCESS(rv, rv);
1262 :
1263 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1264 : "CREATE TABLE index_data ("
1265 : "index_id INTEGER NOT NULL, "
1266 : "value BLOB NOT NULL, "
1267 : "object_data_key BLOB NOT NULL, "
1268 : "object_data_id INTEGER NOT NULL, "
1269 : "PRIMARY KEY (index_id, value, object_data_key), "
1270 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1271 : "CASCADE, "
1272 : "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1273 : "CASCADE"
1274 : ");"
1275 0 : ));
1276 0 : NS_ENSURE_SUCCESS(rv, rv);
1277 :
1278 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1279 : "INSERT INTO index_data "
1280 : "SELECT index_id, value, object_data_key, object_data_id "
1281 : "FROM temp_upgrade;"
1282 0 : ));
1283 0 : NS_ENSURE_SUCCESS(rv, rv);
1284 :
1285 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1286 : "DROP TABLE temp_upgrade;"
1287 0 : ));
1288 0 : NS_ENSURE_SUCCESS(rv, rv);
1289 :
1290 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1291 : "CREATE INDEX index_data_object_data_id_index "
1292 : "ON index_data (object_data_id);"
1293 0 : ));
1294 0 : NS_ENSURE_SUCCESS(rv, rv);
1295 :
1296 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1297 : "CREATE TEMPORARY TABLE temp_upgrade ("
1298 : "index_id, "
1299 : "value, "
1300 : "object_data_key, "
1301 : "object_data_id "
1302 : ");"
1303 0 : ));
1304 0 : NS_ENSURE_SUCCESS(rv, rv);
1305 :
1306 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1307 : "INSERT INTO temp_upgrade "
1308 : "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
1309 : "FROM unique_index_data;"
1310 0 : ));
1311 0 : NS_ENSURE_SUCCESS(rv, rv);
1312 :
1313 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1314 : "DROP TABLE unique_index_data;"
1315 0 : ));
1316 0 : NS_ENSURE_SUCCESS(rv, rv);
1317 :
1318 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1319 : "CREATE TABLE unique_index_data ("
1320 : "index_id INTEGER NOT NULL, "
1321 : "value BLOB NOT NULL, "
1322 : "object_data_key BLOB NOT NULL, "
1323 : "object_data_id INTEGER NOT NULL, "
1324 : "PRIMARY KEY (index_id, value, object_data_key), "
1325 : "UNIQUE (index_id, value), "
1326 : "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1327 : "CASCADE "
1328 : "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1329 : "CASCADE"
1330 : ");"
1331 0 : ));
1332 0 : NS_ENSURE_SUCCESS(rv, rv);
1333 :
1334 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1335 : "INSERT INTO unique_index_data "
1336 : "SELECT index_id, value, object_data_key, object_data_id "
1337 : "FROM temp_upgrade;"
1338 0 : ));
1339 0 : NS_ENSURE_SUCCESS(rv, rv);
1340 :
1341 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1342 : "DROP TABLE temp_upgrade;"
1343 0 : ));
1344 0 : NS_ENSURE_SUCCESS(rv, rv);
1345 :
1346 0 : rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1347 : "CREATE INDEX unique_index_data_object_data_id_index "
1348 : "ON unique_index_data (object_data_id);"
1349 0 : ));
1350 0 : NS_ENSURE_SUCCESS(rv, rv);
1351 :
1352 0 : rv = aConnection->RemoveFunction(encoderName);
1353 0 : NS_ENSURE_SUCCESS(rv, rv);
1354 :
1355 0 : rv = aConnection->SetSchemaVersion(MakeSchemaVersion(12, 0));
1356 0 : NS_ENSURE_SUCCESS(rv, rv);
1357 :
1358 0 : return NS_OK;
1359 : }
1360 :
1361 : class VersionChangeEventsRunnable;
1362 :
1363 : class SetVersionHelper : public AsyncConnectionHelper,
1364 : public IDBTransactionListener
1365 284 : {
1366 : friend class VersionChangeEventsRunnable;
1367 : public:
1368 71 : SetVersionHelper(IDBTransaction* aTransaction,
1369 : IDBOpenDBRequest* aRequest,
1370 : OpenDatabaseHelper* aHelper,
1371 : PRUint64 aRequestedVersion,
1372 : PRUint64 aCurrentVersion)
1373 : : AsyncConnectionHelper(aTransaction, aRequest),
1374 : mOpenRequest(aRequest), mOpenHelper(aHelper),
1375 : mRequestedVersion(aRequestedVersion),
1376 71 : mCurrentVersion(aCurrentVersion)
1377 : {
1378 71 : mTransaction->SetTransactionListener(this);
1379 71 : }
1380 :
1381 : NS_DECL_ISUPPORTS_INHERITED
1382 :
1383 : nsresult GetSuccessResult(JSContext* aCx,
1384 : jsval* aVal);
1385 :
1386 : protected:
1387 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
1388 : nsresult Init();
1389 :
1390 : // SetVersionHelper never fires an error event at the request. It hands that
1391 : // responsibility back to the OpenDatabaseHelper
1392 0 : void OnError() { }
1393 :
1394 : // Need an upgradeneeded event here.
1395 : already_AddRefed<nsDOMEvent> CreateSuccessEvent();
1396 :
1397 : nsresult NotifyTransactionComplete(IDBTransaction* aTransaction);
1398 :
1399 3 : PRUint64 RequestedVersion() const
1400 : {
1401 3 : return mRequestedVersion;
1402 : }
1403 :
1404 : private:
1405 : // In-params
1406 : nsRefPtr<IDBOpenDBRequest> mOpenRequest;
1407 : nsRefPtr<OpenDatabaseHelper> mOpenHelper;
1408 : PRUint64 mRequestedVersion;
1409 : PRUint64 mCurrentVersion;
1410 : };
1411 :
1412 : class DeleteDatabaseHelper : public AsyncConnectionHelper
1413 0 : {
1414 : friend class VersionChangeEventsRunnable;
1415 : public:
1416 0 : DeleteDatabaseHelper(IDBOpenDBRequest* aRequest,
1417 : OpenDatabaseHelper* aHelper,
1418 : PRUint64 aCurrentVersion,
1419 : const nsAString& aName,
1420 : const nsACString& aASCIIOrigin)
1421 : : AsyncConnectionHelper(static_cast<IDBDatabase*>(nsnull), aRequest),
1422 : mOpenHelper(aHelper), mOpenRequest(aRequest),
1423 : mCurrentVersion(aCurrentVersion), mName(aName),
1424 0 : mASCIIOrigin(aASCIIOrigin)
1425 0 : { }
1426 :
1427 : nsresult GetSuccessResult(JSContext* aCx,
1428 : jsval* aVal);
1429 :
1430 0 : void ReleaseMainThreadObjects()
1431 : {
1432 0 : mOpenHelper = nsnull;
1433 0 : mOpenRequest = nsnull;
1434 :
1435 0 : AsyncConnectionHelper::ReleaseMainThreadObjects();
1436 0 : }
1437 :
1438 : protected:
1439 : nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
1440 : nsresult Init();
1441 :
1442 : // DeleteDatabaseHelper never fires events at the request. It hands that
1443 : // responsibility back to the OpenDatabaseHelper
1444 0 : void OnError()
1445 : {
1446 0 : mOpenHelper->NotifyDeleteFinished();
1447 0 : }
1448 0 : nsresult OnSuccess()
1449 : {
1450 0 : return mOpenHelper->NotifyDeleteFinished();
1451 : }
1452 :
1453 0 : PRUint64 RequestedVersion() const
1454 : {
1455 0 : return 0;
1456 : }
1457 : private:
1458 : // In-params
1459 : nsRefPtr<OpenDatabaseHelper> mOpenHelper;
1460 : nsRefPtr<IDBOpenDBRequest> mOpenRequest;
1461 : PRUint64 mCurrentVersion;
1462 : nsString mName;
1463 : nsCString mASCIIOrigin;
1464 : };
1465 :
1466 : // Responsible for firing "versionchange" events at all live and non-closed
1467 : // databases, and for firing a "blocked" event at the requesting database if any
1468 : // databases fail to close.
1469 : class VersionChangeEventsRunnable : public nsRunnable
1470 12 : {
1471 : public:
1472 3 : VersionChangeEventsRunnable(
1473 : IDBDatabase* aRequestingDatabase,
1474 : IDBOpenDBRequest* aRequest,
1475 : nsTArray<nsRefPtr<IDBDatabase> >& aWaitingDatabases,
1476 : PRInt64 aOldVersion,
1477 : PRInt64 aNewVersion)
1478 : : mRequestingDatabase(aRequestingDatabase),
1479 : mRequest(aRequest),
1480 : mOldVersion(aOldVersion),
1481 3 : mNewVersion(aNewVersion)
1482 : {
1483 3 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1484 3 : NS_ASSERTION(aRequestingDatabase, "Null pointer!");
1485 3 : NS_ASSERTION(aRequest, "Null pointer!");
1486 :
1487 3 : if (!mWaitingDatabases.SwapElements(aWaitingDatabases)) {
1488 0 : NS_ERROR("This should never fail!");
1489 : }
1490 3 : }
1491 :
1492 3 : NS_IMETHOD Run()
1493 : {
1494 3 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
1495 :
1496 : // Fire version change events at all of the databases that are not already
1497 : // closed. Also kick bfcached documents out of bfcache.
1498 3 : PRUint32 count = mWaitingDatabases.Length();
1499 7 : for (PRUint32 index = 0; index < count; index++) {
1500 4 : nsRefPtr<IDBDatabase>& database = mWaitingDatabases[index];
1501 :
1502 4 : if (database->IsClosed()) {
1503 0 : continue;
1504 : }
1505 :
1506 : // First check if the document the IDBDatabase is part of is bfcached.
1507 8 : nsCOMPtr<nsIDocument> ownerDoc = database->GetOwnerDocument();
1508 : nsIBFCacheEntry* bfCacheEntry;
1509 4 : if (ownerDoc && (bfCacheEntry = ownerDoc->GetBFCacheEntry())) {
1510 0 : bfCacheEntry->RemoveFromBFCacheSync();
1511 0 : NS_ASSERTION(database->IsClosed(),
1512 : "Kicking doc out of bfcache should have closed database");
1513 0 : continue;
1514 : }
1515 :
1516 : // Otherwise fire a versionchange event.
1517 : nsRefPtr<nsDOMEvent> event =
1518 8 : IDBVersionChangeEvent::Create(mOldVersion, mNewVersion);
1519 4 : NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
1520 :
1521 : bool dummy;
1522 8 : database->DispatchEvent(event, &dummy);
1523 : }
1524 :
1525 : // Now check to see if any didn't close. If there are some running still
1526 : // then fire the blocked event.
1527 5 : for (PRUint32 index = 0; index < count; index++) {
1528 3 : if (!mWaitingDatabases[index]->IsClosed()) {
1529 : nsRefPtr<nsDOMEvent> event =
1530 2 : IDBVersionChangeEvent::CreateBlocked(mOldVersion, mNewVersion);
1531 1 : NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
1532 :
1533 : bool dummy;
1534 1 : mRequest->DispatchEvent(event, &dummy);
1535 :
1536 1 : break;
1537 : }
1538 : }
1539 :
1540 3 : return NS_OK;
1541 : }
1542 :
1543 : template <class T>
1544 : static
1545 3 : void QueueVersionChange(nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
1546 : void* aClosure);
1547 : private:
1548 : nsRefPtr<IDBDatabase> mRequestingDatabase;
1549 : nsRefPtr<IDBOpenDBRequest> mRequest;
1550 : nsTArray<nsRefPtr<IDBDatabase> > mWaitingDatabases;
1551 : PRInt64 mOldVersion;
1552 : PRInt64 mNewVersion;
1553 : };
1554 :
1555 : } // anonymous namespace
1556 :
1557 1561 : NS_IMPL_THREADSAFE_ISUPPORTS1(OpenDatabaseHelper, nsIRunnable);
1558 :
1559 : nsresult
1560 76 : OpenDatabaseHelper::Init()
1561 : {
1562 152 : nsCString str(mASCIIOrigin);
1563 76 : str.Append("*");
1564 76 : str.Append(NS_ConvertUTF16toUTF8(mName));
1565 :
1566 152 : nsCOMPtr<nsIAtom> atom = do_GetAtom(str);
1567 76 : NS_ENSURE_TRUE(atom, NS_ERROR_FAILURE);
1568 :
1569 76 : atom.swap(mDatabaseId);
1570 76 : return NS_OK;
1571 : }
1572 :
1573 : nsresult
1574 76 : OpenDatabaseHelper::Dispatch(nsIEventTarget* aTarget)
1575 : {
1576 76 : NS_ASSERTION(mState == eCreated, "We've already been dispatched?");
1577 76 : mState = eDBWork;
1578 :
1579 76 : return aTarget->Dispatch(this, NS_DISPATCH_NORMAL);
1580 : }
1581 :
1582 : nsresult
1583 0 : OpenDatabaseHelper::RunImmediately()
1584 : {
1585 0 : NS_ASSERTION(mState == eCreated, "We've already been dispatched?");
1586 0 : NS_ASSERTION(NS_FAILED(mResultCode),
1587 : "Should only be short-circuiting if we failed!");
1588 0 : NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!");
1589 :
1590 0 : mState = eFiringEvents;
1591 0 : return this->Run();
1592 : }
1593 :
1594 : nsresult
1595 76 : OpenDatabaseHelper::DoDatabaseWork()
1596 : {
1597 : #ifdef DEBUG
1598 : {
1599 : bool correctThread;
1600 76 : NS_ASSERTION(NS_SUCCEEDED(IndexedDatabaseManager::Get()->IOThread()->
1601 : IsOnCurrentThread(&correctThread)) &&
1602 : correctThread,
1603 : "Running on the wrong thread!");
1604 : }
1605 : #endif
1606 :
1607 76 : mState = eFiringEvents; // In case we fail somewhere along the line.
1608 :
1609 76 : if (IndexedDatabaseManager::IsShuttingDown()) {
1610 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1611 : }
1612 :
1613 76 : NS_ASSERTION(mOpenDBRequest, "This should never be null!");
1614 :
1615 : // This will be null for non-window contexts.
1616 76 : nsPIDOMWindow* window = mOpenDBRequest->GetOwner();
1617 :
1618 152 : AutoEnterWindow autoWindow(window);
1619 :
1620 152 : nsCOMPtr<nsIFile> dbDirectory;
1621 :
1622 76 : IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
1623 76 : NS_ASSERTION(mgr, "This should never be null!");
1624 :
1625 : nsresult rv = mgr->EnsureOriginIsInitialized(mASCIIOrigin,
1626 76 : getter_AddRefs(dbDirectory));
1627 76 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1628 :
1629 152 : nsAutoString filename;
1630 76 : rv = GetDatabaseFilename(mName, filename);
1631 76 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1632 :
1633 152 : nsCOMPtr<nsIFile> dbFile;
1634 76 : rv = dbDirectory->Clone(getter_AddRefs(dbFile));
1635 76 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1636 :
1637 76 : rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
1638 76 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1639 :
1640 76 : rv = dbFile->GetPath(mDatabaseFilePath);
1641 76 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1642 :
1643 152 : nsCOMPtr<nsIFile> fileManagerDirectory;
1644 76 : rv = dbDirectory->Clone(getter_AddRefs(fileManagerDirectory));
1645 76 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1646 :
1647 76 : rv = fileManagerDirectory->Append(filename);
1648 76 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1649 :
1650 152 : nsCOMPtr<mozIStorageConnection> connection;
1651 : rv = CreateDatabaseConnection(mName, dbFile, fileManagerDirectory,
1652 76 : getter_AddRefs(connection));
1653 76 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1654 :
1655 : rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId,
1656 76 : &mCurrentVersion, mObjectStores);
1657 76 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1658 :
1659 76 : if (mForDeletion) {
1660 0 : mState = eDeletePending;
1661 0 : return NS_OK;
1662 : }
1663 :
1664 99 : for (PRUint32 i = 0; i < mObjectStores.Length(); i++) {
1665 23 : nsRefPtr<ObjectStoreInfo>& objectStoreInfo = mObjectStores[i];
1666 41 : for (PRUint32 j = 0; j < objectStoreInfo->indexes.Length(); j++) {
1667 18 : IndexInfo& indexInfo = objectStoreInfo->indexes[j];
1668 18 : mLastIndexId = NS_MAX(indexInfo.id, mLastIndexId);
1669 : }
1670 23 : mLastObjectStoreId = NS_MAX(objectStoreInfo->id, mLastObjectStoreId);
1671 : }
1672 :
1673 : // See if we need to do a VERSION_CHANGE transaction
1674 :
1675 : // Optional version semantics.
1676 76 : if (!mRequestedVersion) {
1677 : // If the requested version was not specified and the database was created,
1678 : // treat it as if version 1 were requested.
1679 0 : if (mCurrentVersion == 0) {
1680 0 : mRequestedVersion = 1;
1681 : }
1682 : else {
1683 : // Otherwise, treat it as if the current version were requested.
1684 0 : mRequestedVersion = mCurrentVersion;
1685 : }
1686 : }
1687 :
1688 76 : if (mCurrentVersion > mRequestedVersion) {
1689 1 : return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR;
1690 : }
1691 :
1692 75 : if (mCurrentVersion != mRequestedVersion) {
1693 71 : mState = eSetVersionPending;
1694 : }
1695 :
1696 75 : mFileManager = mgr->GetOrCreateFileManager(mASCIIOrigin, mName);
1697 75 : NS_ENSURE_TRUE(mFileManager, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1698 :
1699 75 : if (!mFileManager->Inited()) {
1700 52 : rv = mFileManager->Init(fileManagerDirectory, connection);
1701 52 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1702 : }
1703 :
1704 75 : if (!mFileManager->Loaded()) {
1705 52 : rv = mFileManager->Load(connection);
1706 52 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1707 : }
1708 :
1709 75 : return NS_OK;
1710 : }
1711 :
1712 : // static
1713 : nsresult
1714 76 : OpenDatabaseHelper::CreateDatabaseConnection(
1715 : const nsAString& aName,
1716 : nsIFile* aDBFile,
1717 : nsIFile* aFileManagerDirectory,
1718 : mozIStorageConnection** aConnection)
1719 : {
1720 76 : NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
1721 :
1722 152 : NS_NAMED_LITERAL_CSTRING(quotaVFSName, "quota");
1723 :
1724 : nsCOMPtr<mozIStorageServiceQuotaManagement> ss =
1725 152 : do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
1726 76 : NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
1727 :
1728 152 : nsCOMPtr<mozIStorageConnection> connection;
1729 76 : nsresult rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName,
1730 76 : getter_AddRefs(connection));
1731 76 : if (rv == NS_ERROR_FILE_CORRUPTED) {
1732 : // If we're just opening the database during origin initialization, then
1733 : // we don't want to erase any files. The failure here will fail origin
1734 : // initialization too.
1735 0 : if (aName.IsVoid()) {
1736 0 : return rv;
1737 : }
1738 :
1739 : // Nuke the database file. The web services can recreate their data.
1740 0 : rv = aDBFile->Remove(false);
1741 0 : NS_ENSURE_SUCCESS(rv, rv);
1742 :
1743 : bool exists;
1744 0 : rv = aFileManagerDirectory->Exists(&exists);
1745 0 : NS_ENSURE_SUCCESS(rv, rv);
1746 :
1747 0 : if (exists) {
1748 : bool isDirectory;
1749 0 : rv = aFileManagerDirectory->IsDirectory(&isDirectory);
1750 0 : NS_ENSURE_SUCCESS(rv, rv);
1751 0 : NS_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1752 :
1753 0 : rv = aFileManagerDirectory->Remove(true);
1754 0 : NS_ENSURE_SUCCESS(rv, rv);
1755 : }
1756 :
1757 0 : rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName,
1758 0 : getter_AddRefs(connection));
1759 : }
1760 76 : NS_ENSURE_SUCCESS(rv, rv);
1761 :
1762 76 : rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem"));
1763 76 : NS_ENSURE_SUCCESS(rv, rv);
1764 :
1765 : // Check to make sure that the database schema is correct.
1766 : PRInt32 schemaVersion;
1767 76 : rv = connection->GetSchemaVersion(&schemaVersion);
1768 76 : NS_ENSURE_SUCCESS(rv, rv);
1769 :
1770 : // Unknown schema will fail origin initialization too
1771 76 : if (!schemaVersion && aName.IsVoid()) {
1772 0 : NS_WARNING("Unable to open IndexedDB database, schema is not set!");
1773 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1774 : }
1775 :
1776 76 : if (schemaVersion > kSQLiteSchemaVersion) {
1777 0 : NS_WARNING("Unable to open IndexedDB database, schema is too high!");
1778 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1779 : }
1780 :
1781 76 : bool vacuumNeeded = false;
1782 :
1783 76 : if (schemaVersion != kSQLiteSchemaVersion) {
1784 : mozStorageTransaction transaction(connection, false,
1785 104 : mozIStorageConnection::TRANSACTION_IMMEDIATE);
1786 :
1787 52 : if (!schemaVersion) {
1788 : // Brand new file, initialize our tables.
1789 52 : rv = CreateTables(connection);
1790 52 : NS_ENSURE_SUCCESS(rv, rv);
1791 :
1792 52 : NS_ASSERTION(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)) &&
1793 : schemaVersion == kSQLiteSchemaVersion,
1794 : "CreateTables set a bad schema version!");
1795 :
1796 104 : nsCOMPtr<mozIStorageStatement> stmt;
1797 104 : nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING(
1798 : "INSERT INTO database (name) "
1799 : "VALUES (:name)"
1800 104 : ), getter_AddRefs(stmt));
1801 52 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1802 :
1803 52 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
1804 52 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1805 :
1806 52 : rv = stmt->Execute();
1807 52 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1808 : }
1809 : else {
1810 : // This logic needs to change next time we change the schema!
1811 : PR_STATIC_ASSERT(kSQLiteSchemaVersion == PRInt32((12 << 4) + 0));
1812 :
1813 0 : while (schemaVersion != kSQLiteSchemaVersion) {
1814 0 : if (schemaVersion == 4) {
1815 0 : rv = UpgradeSchemaFrom4To5(connection);
1816 : }
1817 0 : else if (schemaVersion == 5) {
1818 0 : rv = UpgradeSchemaFrom5To6(connection);
1819 : }
1820 0 : else if (schemaVersion == 6) {
1821 0 : rv = UpgradeSchemaFrom6To7(connection);
1822 : }
1823 0 : else if (schemaVersion == 7) {
1824 0 : rv = UpgradeSchemaFrom7To8(connection);
1825 : }
1826 0 : else if (schemaVersion == 8) {
1827 0 : rv = UpgradeSchemaFrom8To9_0(connection);
1828 0 : vacuumNeeded = true;
1829 : }
1830 0 : else if (schemaVersion == MakeSchemaVersion(9, 0)) {
1831 0 : rv = UpgradeSchemaFrom9_0To10_0(connection);
1832 : }
1833 0 : else if (schemaVersion == MakeSchemaVersion(10, 0)) {
1834 0 : rv = UpgradeSchemaFrom10_0To11_0(connection);
1835 : }
1836 0 : else if (schemaVersion == MakeSchemaVersion(11, 0)) {
1837 0 : rv = UpgradeSchemaFrom11_0To12_0(connection);
1838 : }
1839 : else {
1840 : NS_WARNING("Unable to open IndexedDB database, no upgrade path is "
1841 0 : "available!");
1842 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
1843 : }
1844 0 : NS_ENSURE_SUCCESS(rv, rv);
1845 :
1846 0 : rv = connection->GetSchemaVersion(&schemaVersion);
1847 0 : NS_ENSURE_SUCCESS(rv, rv);
1848 : }
1849 :
1850 0 : NS_ASSERTION(schemaVersion == kSQLiteSchemaVersion, "Huh?!");
1851 : }
1852 :
1853 52 : rv = transaction.Commit();
1854 52 : NS_ENSURE_SUCCESS(rv, rv);
1855 : }
1856 :
1857 76 : if (vacuumNeeded) {
1858 0 : rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1859 : "VACUUM;"
1860 0 : ));
1861 0 : NS_ENSURE_SUCCESS(rv, rv);
1862 : }
1863 :
1864 : // Turn on foreign key constraints.
1865 152 : rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1866 : "PRAGMA foreign_keys = ON;"
1867 76 : ));
1868 76 : NS_ENSURE_SUCCESS(rv, rv);
1869 :
1870 76 : connection.forget(aConnection);
1871 76 : return NS_OK;
1872 : }
1873 :
1874 : nsresult
1875 71 : OpenDatabaseHelper::StartSetVersion()
1876 : {
1877 71 : NS_ASSERTION(mState == eSetVersionPending, "Why are we here?");
1878 :
1879 : // In case we fail, fire error events
1880 71 : mState = eFiringEvents;
1881 :
1882 71 : nsresult rv = EnsureSuccessResult();
1883 71 : NS_ENSURE_SUCCESS(rv, rv);
1884 :
1885 142 : nsTArray<nsString> storesToOpen;
1886 : nsRefPtr<IDBTransaction> transaction =
1887 : IDBTransaction::Create(mDatabase, storesToOpen,
1888 142 : IDBTransaction::VERSION_CHANGE, true);
1889 71 : NS_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1890 :
1891 : nsRefPtr<SetVersionHelper> helper =
1892 : new SetVersionHelper(transaction, mOpenDBRequest, this, mRequestedVersion,
1893 213 : mCurrentVersion);
1894 :
1895 71 : IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
1896 71 : NS_ASSERTION(mgr, "This should never be null!");
1897 :
1898 : rv = mgr->AcquireExclusiveAccess(mDatabase, helper,
1899 : &VersionChangeEventsRunnable::QueueVersionChange<SetVersionHelper>,
1900 71 : helper);
1901 71 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1902 :
1903 : // The SetVersionHelper is responsible for dispatching us back to the
1904 : // main thread again and changing the state to eSetVersionCompleted.
1905 71 : mState = eSetVersionPending;
1906 :
1907 71 : return NS_OK;
1908 : }
1909 :
1910 : nsresult
1911 0 : OpenDatabaseHelper::StartDelete()
1912 : {
1913 0 : NS_ASSERTION(mState == eDeletePending, "Why are we here?");
1914 :
1915 : // In case we fail, fire error events
1916 0 : mState = eFiringEvents;
1917 :
1918 0 : nsresult rv = EnsureSuccessResult();
1919 0 : NS_ENSURE_SUCCESS(rv, rv);
1920 :
1921 : nsRefPtr<DeleteDatabaseHelper> helper =
1922 : new DeleteDatabaseHelper(mOpenDBRequest, this, mCurrentVersion, mName,
1923 0 : mASCIIOrigin);
1924 :
1925 0 : IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
1926 0 : NS_ASSERTION(mgr, "This should never be null!");
1927 :
1928 : rv = mgr->AcquireExclusiveAccess(mDatabase, helper,
1929 : &VersionChangeEventsRunnable::QueueVersionChange<DeleteDatabaseHelper>,
1930 0 : helper);
1931 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
1932 :
1933 : // The DeleteDatabaseHelper is responsible for dispatching us back to the
1934 : // main thread again and changing the state to eDeleteCompleted.
1935 0 : mState = eDeletePending;
1936 0 : return NS_OK;
1937 : }
1938 :
1939 : NS_IMETHODIMP
1940 223 : OpenDatabaseHelper::Run()
1941 : {
1942 223 : NS_ASSERTION(mState != eCreated, "Dispatch was not called?!?");
1943 :
1944 223 : if (NS_IsMainThread()) {
1945 : // If we need to queue up a SetVersionHelper, do that here.
1946 147 : if (mState == eSetVersionPending) {
1947 71 : nsresult rv = StartSetVersion();
1948 :
1949 71 : if (NS_SUCCEEDED(rv)) {
1950 71 : return rv;
1951 : }
1952 :
1953 0 : SetError(rv);
1954 : // fall through and run the default error processing
1955 : }
1956 76 : else if (mState == eDeletePending) {
1957 0 : nsresult rv = StartDelete();
1958 :
1959 0 : if (NS_SUCCEEDED(rv)) {
1960 0 : return rv;
1961 : }
1962 :
1963 0 : SetError(rv);
1964 : // fall through and run the default error processing
1965 : }
1966 :
1967 : // We've done whatever work we need to do on the DB thread, and any
1968 : // SetVersion/DeleteDatabase stuff is done by now.
1969 76 : NS_ASSERTION(mState == eFiringEvents ||
1970 : mState == eSetVersionCompleted ||
1971 : mState == eDeleteCompleted, "Why are we here?");
1972 :
1973 76 : switch (mState) {
1974 : case eSetVersionCompleted: {
1975 : // Allow transaction creation/other version change transactions to proceed
1976 : // before we fire events. Other version changes will be postd to the end
1977 : // of the event loop, and will be behind whatever the page does in
1978 : // its error/success event handlers.
1979 71 : mDatabase->ExitSetVersionTransaction();
1980 :
1981 71 : mState = eFiringEvents;
1982 71 : break;
1983 : }
1984 :
1985 : case eDeleteCompleted: {
1986 : // Destroy the database now (we should have the only ref).
1987 0 : mDatabase = nsnull;
1988 :
1989 0 : DatabaseInfo::Remove(mDatabaseId);
1990 :
1991 0 : IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
1992 0 : NS_ASSERTION(mgr, "This should never fail!");
1993 :
1994 0 : mgr->InvalidateFileManager(mASCIIOrigin, mName);
1995 :
1996 0 : mState = eFiringEvents;
1997 0 : break;
1998 : }
1999 :
2000 : case eFiringEvents: {
2001 : // Notify the request that we're done, but only if we didn't just
2002 : // finish a [SetVersion/DeleteDatabase]Helper. In that case, the
2003 : // helper tells the request that it is done, and we avoid calling
2004 : // NotifyHelperCompleted twice.
2005 :
2006 5 : nsresult rv = mOpenDBRequest->NotifyHelperCompleted(this);
2007 5 : if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) {
2008 0 : mResultCode = rv;
2009 : }
2010 5 : break;
2011 : }
2012 :
2013 : default:
2014 0 : NS_NOTREACHED("Shouldn't get here!");
2015 : }
2016 :
2017 76 : NS_ASSERTION(mState == eFiringEvents, "Why are we here?");
2018 :
2019 76 : if (NS_FAILED(mResultCode)) {
2020 2 : DispatchErrorEvent();
2021 : } else {
2022 74 : DispatchSuccessEvent();
2023 : }
2024 :
2025 76 : IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
2026 76 : NS_ASSERTION(manager, "This should never be null!");
2027 :
2028 76 : manager->AllowNextSynchronizedOp(mASCIIOrigin, mDatabaseId);
2029 :
2030 76 : ReleaseMainThreadObjects();
2031 :
2032 76 : return NS_OK;
2033 : }
2034 :
2035 : // If we're on the DB thread, do that
2036 76 : NS_ASSERTION(mState == eDBWork, "Why are we here?");
2037 76 : mResultCode = DoDatabaseWork();
2038 76 : NS_ASSERTION(mState != eDBWork, "We should be doing something else now.");
2039 :
2040 76 : return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
2041 : }
2042 :
2043 : nsresult
2044 75 : OpenDatabaseHelper::EnsureSuccessResult()
2045 : {
2046 150 : nsRefPtr<DatabaseInfo> dbInfo;
2047 75 : if (DatabaseInfo::Get(mDatabaseId, getter_AddRefs(dbInfo))) {
2048 :
2049 : #ifdef DEBUG
2050 : {
2051 21 : NS_ASSERTION(dbInfo->name == mName &&
2052 : dbInfo->version == mCurrentVersion &&
2053 : dbInfo->id == mDatabaseId &&
2054 : dbInfo->filePath == mDatabaseFilePath,
2055 : "Metadata mismatch!");
2056 :
2057 21 : PRUint32 objectStoreCount = mObjectStores.Length();
2058 43 : for (PRUint32 index = 0; index < objectStoreCount; index++) {
2059 22 : nsRefPtr<ObjectStoreInfo>& info = mObjectStores[index];
2060 :
2061 22 : ObjectStoreInfo* otherInfo = dbInfo->GetObjectStore(info->name);
2062 22 : NS_ASSERTION(otherInfo, "ObjectStore not known!");
2063 :
2064 22 : NS_ASSERTION(info->name == otherInfo->name &&
2065 : info->id == otherInfo->id &&
2066 : info->keyPath == otherInfo->keyPath,
2067 : "Metadata mismatch!");
2068 22 : NS_ASSERTION(dbInfo->ContainsStoreName(info->name),
2069 : "Object store names out of date!");
2070 22 : NS_ASSERTION(info->indexes.Length() == otherInfo->indexes.Length(),
2071 : "Bad index length!");
2072 :
2073 22 : PRUint32 indexCount = info->indexes.Length();
2074 40 : for (PRUint32 indexIndex = 0; indexIndex < indexCount; indexIndex++) {
2075 18 : const IndexInfo& indexInfo = info->indexes[indexIndex];
2076 18 : const IndexInfo& otherIndexInfo = otherInfo->indexes[indexIndex];
2077 18 : NS_ASSERTION(indexInfo.id == otherIndexInfo.id,
2078 : "Bad index id!");
2079 18 : NS_ASSERTION(indexInfo.name == otherIndexInfo.name,
2080 : "Bad index name!");
2081 18 : NS_ASSERTION(indexInfo.keyPath == otherIndexInfo.keyPath,
2082 : "Bad index keyPath!");
2083 18 : NS_ASSERTION(indexInfo.unique == otherIndexInfo.unique,
2084 : "Bad index unique value!");
2085 : }
2086 : }
2087 : }
2088 : #endif
2089 :
2090 : }
2091 : else {
2092 108 : nsRefPtr<DatabaseInfo> newInfo(new DatabaseInfo());
2093 :
2094 54 : newInfo->name = mName;
2095 54 : newInfo->origin = mASCIIOrigin;
2096 54 : newInfo->id = mDatabaseId;
2097 54 : newInfo->filePath = mDatabaseFilePath;
2098 :
2099 54 : if (!DatabaseInfo::Put(newInfo)) {
2100 0 : NS_ERROR("Failed to add to hash!");
2101 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2102 : }
2103 :
2104 54 : newInfo.swap(dbInfo);
2105 :
2106 : nsresult rv = IDBFactory::SetDatabaseMetadata(dbInfo, mCurrentVersion,
2107 54 : mObjectStores);
2108 54 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2109 :
2110 54 : NS_ASSERTION(mObjectStores.IsEmpty(), "Should have swapped!");
2111 : }
2112 :
2113 75 : dbInfo->nextObjectStoreId = mLastObjectStoreId + 1;
2114 75 : dbInfo->nextIndexId = mLastIndexId + 1;
2115 :
2116 : nsRefPtr<IDBDatabase> database =
2117 : IDBDatabase::Create(mOpenDBRequest,
2118 : dbInfo.forget(),
2119 : mASCIIOrigin,
2120 150 : mFileManager);
2121 75 : if (!database) {
2122 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2123 : }
2124 :
2125 75 : NS_ASSERTION(!mDatabase, "Shouldn't have a database yet!");
2126 75 : mDatabase.swap(database);
2127 :
2128 75 : return NS_OK;
2129 : }
2130 :
2131 : nsresult
2132 4 : OpenDatabaseHelper::GetSuccessResult(JSContext* aCx,
2133 : jsval* aVal)
2134 : {
2135 : // Be careful not to load the database twice.
2136 4 : if (!mDatabase) {
2137 4 : nsresult rv = EnsureSuccessResult();
2138 4 : NS_ENSURE_SUCCESS(rv, rv);
2139 : }
2140 :
2141 4 : return WrapNative(aCx, NS_ISUPPORTS_CAST(nsIDOMEventTarget*, mDatabase),
2142 4 : aVal);
2143 : }
2144 :
2145 : nsresult
2146 71 : OpenDatabaseHelper::NotifySetVersionFinished()
2147 : {
2148 71 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread");
2149 71 : NS_ASSERTION(mState = eSetVersionPending, "How did we get here?");
2150 :
2151 71 : mState = eSetVersionCompleted;
2152 :
2153 : // Dispatch ourself back to the main thread
2154 71 : return NS_DispatchToCurrentThread(this);
2155 : }
2156 :
2157 : nsresult
2158 0 : OpenDatabaseHelper::NotifyDeleteFinished()
2159 : {
2160 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread");
2161 0 : NS_ASSERTION(mState == eDeletePending, "How did we get here?");
2162 :
2163 0 : mState = eDeleteCompleted;
2164 :
2165 : // Dispatch ourself back to the main thread
2166 0 : return NS_DispatchToCurrentThread(this);
2167 : }
2168 :
2169 : void
2170 71 : OpenDatabaseHelper::BlockDatabase()
2171 : {
2172 71 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2173 71 : NS_ASSERTION(mDatabase, "This is going bad fast.");
2174 :
2175 71 : mDatabase->EnterSetVersionTransaction();
2176 71 : }
2177 :
2178 : void
2179 74 : OpenDatabaseHelper::DispatchSuccessEvent()
2180 : {
2181 : nsRefPtr<nsDOMEvent> event =
2182 74 : CreateGenericEvent(NS_LITERAL_STRING(SUCCESS_EVT_STR),
2183 148 : eDoesNotBubble, eNotCancelable);
2184 74 : if (!event) {
2185 0 : NS_ERROR("Failed to create event!");
2186 : return;
2187 : }
2188 :
2189 : bool dummy;
2190 74 : mOpenDBRequest->DispatchEvent(event, &dummy);
2191 : }
2192 :
2193 : void
2194 2 : OpenDatabaseHelper::DispatchErrorEvent()
2195 : {
2196 : nsRefPtr<nsDOMEvent> event =
2197 2 : CreateGenericEvent(NS_LITERAL_STRING(ERROR_EVT_STR),
2198 4 : eDoesBubble, eCancelable);
2199 2 : if (!event) {
2200 0 : NS_ERROR("Failed to create event!");
2201 : return;
2202 : }
2203 :
2204 2 : PRUint16 errorCode = 0;
2205 : DebugOnly<nsresult> rv =
2206 4 : mOpenDBRequest->GetErrorCode(&errorCode);
2207 2 : NS_ASSERTION(NS_SUCCEEDED(rv), "This shouldn't be failing at this point!");
2208 2 : if (!errorCode) {
2209 1 : mOpenDBRequest->SetError(mResultCode);
2210 : }
2211 :
2212 : bool dummy;
2213 2 : mOpenDBRequest->DispatchEvent(event, &dummy);
2214 : }
2215 :
2216 : void
2217 76 : OpenDatabaseHelper::ReleaseMainThreadObjects()
2218 : {
2219 76 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2220 :
2221 76 : mOpenDBRequest = nsnull;
2222 76 : mDatabase = nsnull;
2223 76 : mDatabaseId = nsnull;
2224 :
2225 76 : HelperBase::ReleaseMainThreadObjects();
2226 76 : }
2227 :
2228 2278 : NS_IMPL_ISUPPORTS_INHERITED0(SetVersionHelper, AsyncConnectionHelper);
2229 :
2230 : nsresult
2231 71 : SetVersionHelper::Init()
2232 : {
2233 : // Block transaction creation until we are done.
2234 71 : mOpenHelper->BlockDatabase();
2235 :
2236 71 : return NS_OK;
2237 : }
2238 :
2239 : nsresult
2240 71 : SetVersionHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
2241 : {
2242 71 : NS_ASSERTION(aConnection, "Passing a null connection!");
2243 :
2244 142 : nsCOMPtr<mozIStorageStatement> stmt;
2245 71 : nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
2246 : "UPDATE database "
2247 : "SET version = :version"
2248 142 : ), getter_AddRefs(stmt));
2249 71 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2250 :
2251 142 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("version"),
2252 71 : mRequestedVersion);
2253 71 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2254 :
2255 71 : if (NS_FAILED(stmt->Execute())) {
2256 0 : return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
2257 : }
2258 :
2259 71 : return NS_OK;
2260 : }
2261 :
2262 : nsresult
2263 71 : SetVersionHelper::GetSuccessResult(JSContext* aCx,
2264 : jsval* aVal)
2265 : {
2266 71 : DatabaseInfo* info = mDatabase->Info();
2267 71 : info->version = mRequestedVersion;
2268 :
2269 71 : NS_ASSERTION(mTransaction, "Better have a transaction!");
2270 :
2271 71 : mOpenRequest->SetTransaction(mTransaction);
2272 :
2273 71 : return WrapNative(aCx, NS_ISUPPORTS_CAST(nsIDOMEventTarget*, mDatabase),
2274 71 : aVal);
2275 : }
2276 :
2277 : // static
2278 : template <class T>
2279 : void
2280 : VersionChangeEventsRunnable::QueueVersionChange(
2281 : nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
2282 : void* aClosure)
2283 : {
2284 3 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2285 3 : NS_ASSERTION(!aDatabases.IsEmpty(), "Why are we here?");
2286 :
2287 3 : T* closure = static_cast<T*>(aClosure);
2288 :
2289 : nsRefPtr<VersionChangeEventsRunnable> eventsRunnable =
2290 : new VersionChangeEventsRunnable(closure->mOpenHelper->Database(),
2291 : closure->mOpenRequest,
2292 : aDatabases,
2293 : closure->mCurrentVersion,
2294 9 : closure->RequestedVersion());
2295 :
2296 3 : NS_DispatchToCurrentThread(eventsRunnable);
2297 3 : }
2298 :
2299 : already_AddRefed<nsDOMEvent>
2300 71 : SetVersionHelper::CreateSuccessEvent()
2301 : {
2302 71 : NS_ASSERTION(mCurrentVersion < mRequestedVersion, "Huh?");
2303 :
2304 : return IDBVersionChangeEvent::CreateUpgradeNeeded(mCurrentVersion,
2305 71 : mRequestedVersion);
2306 : }
2307 :
2308 : nsresult
2309 71 : SetVersionHelper::NotifyTransactionComplete(IDBTransaction* aTransaction)
2310 : {
2311 71 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
2312 71 : NS_ASSERTION(aTransaction, "This is unexpected.");
2313 71 : NS_ASSERTION(mOpenRequest, "Why don't we have a request?");
2314 :
2315 : // If we hit an error, the OpenDatabaseHelper needs to get that error too.
2316 71 : nsresult rv = GetResultCode();
2317 71 : if (NS_FAILED(rv)) {
2318 0 : mOpenHelper->SetError(rv);
2319 : }
2320 :
2321 : // If the transaction was aborted, we should throw an error message.
2322 71 : if (aTransaction->IsAborted()) {
2323 1 : mOpenHelper->SetError(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
2324 : }
2325 :
2326 71 : mOpenRequest->SetTransaction(nsnull);
2327 71 : mOpenRequest = nsnull;
2328 :
2329 71 : rv = mOpenHelper->NotifySetVersionFinished();
2330 71 : mOpenHelper = nsnull;
2331 :
2332 71 : return rv;
2333 : }
2334 :
2335 : nsresult
2336 0 : DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
2337 : {
2338 0 : NS_ASSERTION(!aConnection, "How did we get a connection here?");
2339 :
2340 0 : nsCOMPtr<nsIFile> directory;
2341 : nsresult rv = IDBFactory::GetDirectoryForOrigin(mASCIIOrigin,
2342 0 : getter_AddRefs(directory));
2343 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2344 :
2345 0 : NS_ASSERTION(directory, "What?");
2346 :
2347 0 : nsAutoString filename;
2348 0 : rv = GetDatabaseFilename(mName, filename);
2349 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2350 :
2351 0 : nsCOMPtr<nsIFile> dbFile;
2352 0 : rv = directory->Clone(getter_AddRefs(dbFile));
2353 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2354 :
2355 0 : rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
2356 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2357 :
2358 0 : bool exists = false;
2359 0 : rv = dbFile->Exists(&exists);
2360 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2361 :
2362 : int rc;
2363 :
2364 0 : if (exists) {
2365 0 : nsString dbFilePath;
2366 0 : rv = dbFile->GetPath(dbFilePath);
2367 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2368 :
2369 0 : rc = sqlite3_quota_remove(NS_ConvertUTF16toUTF8(dbFilePath).get());
2370 0 : if (rc != SQLITE_OK) {
2371 0 : NS_WARNING("Failed to delete db file!");
2372 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2373 : }
2374 : }
2375 :
2376 0 : nsCOMPtr<nsIFile> fileManagerDirectory;
2377 0 : rv = directory->Clone(getter_AddRefs(fileManagerDirectory));
2378 0 : NS_ENSURE_SUCCESS(rv, rv);
2379 :
2380 0 : rv = fileManagerDirectory->Append(filename);
2381 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2382 :
2383 0 : rv = fileManagerDirectory->Exists(&exists);
2384 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2385 :
2386 0 : if (exists) {
2387 : bool isDirectory;
2388 0 : rv = fileManagerDirectory->IsDirectory(&isDirectory);
2389 0 : NS_ENSURE_SUCCESS(rv, rv);
2390 0 : NS_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2391 :
2392 0 : nsString fileManagerDirectoryPath;
2393 0 : rv = fileManagerDirectory->GetPath(fileManagerDirectoryPath);
2394 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
2395 :
2396 : rc = sqlite3_quota_remove(
2397 0 : NS_ConvertUTF16toUTF8(fileManagerDirectoryPath).get());
2398 0 : if (rc != SQLITE_OK) {
2399 0 : NS_WARNING("Failed to delete file directory!");
2400 0 : return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
2401 : }
2402 :
2403 0 : rv = fileManagerDirectory->Remove(true);
2404 0 : NS_ENSURE_SUCCESS(rv, rv);
2405 : }
2406 :
2407 0 : return NS_OK;
2408 : }
2409 :
2410 : nsresult
2411 0 : DeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, jsval* aVal)
2412 : {
2413 0 : return NS_OK;
2414 : }
2415 :
2416 : nsresult
2417 0 : DeleteDatabaseHelper::Init()
2418 : {
2419 : // Note that there's no need to block the database here, since the page
2420 : // never gets to touch it, and all other databases must be closed.
2421 :
2422 0 : return NS_OK;
2423 : }
|