Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
5
* You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "ActorsParent.h"
8
9
#include <algorithm>
10
#include <numeric>
11
#include <stdint.h> // UINTPTR_MAX, uintptr_t
12
#include "FileInfo.h"
13
#include "FileManager.h"
14
#include "IDBObjectStore.h"
15
#include "IDBTransaction.h"
16
#include "IndexedDatabase.h"
17
#include "IndexedDatabaseInlines.h"
18
#include "IndexedDatabaseManager.h"
19
#include "InitializedOnce.h"
20
#include "js/StructuredClone.h"
21
#include "js/Value.h"
22
#include "jsapi.h"
23
#include "KeyPath.h"
24
#include "mozilla/Attributes.h"
25
#include "mozilla/AutoRestore.h"
26
#include "mozilla/Casting.h"
27
#include "mozilla/CheckedInt.h"
28
#include "mozilla/CycleCollectedJSRuntime.h"
29
#include "mozilla/EndianUtils.h"
30
#include "mozilla/ErrorNames.h"
31
#include "mozilla/LazyIdleThread.h"
32
#include "mozilla/Maybe.h"
33
#include "mozilla/Preferences.h"
34
#include "mozilla/Result.h"
35
#include "mozilla/SnappyCompressOutputStream.h"
36
#include "mozilla/SnappyUncompressInputStream.h"
37
#include "mozilla/StaticPtr.h"
38
#include "mozilla/storage.h"
39
#include "mozilla/Telemetry.h"
40
#include "mozilla/Unused.h"
41
#include "mozilla/UniquePtrExtensions.h"
42
#include "mozilla/dom/ContentParent.h"
43
#include "mozilla/dom/File.h"
44
#include "mozilla/dom/FileBlobImpl.h"
45
#include "mozilla/dom/StructuredCloneTags.h"
46
#include "mozilla/dom/BrowserParent.h"
47
#include "mozilla/dom/filehandle/ActorsParent.h"
48
#include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h"
49
#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
50
#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h"
51
#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestParent.h"
52
#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h"
53
#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h"
54
#include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h"
55
#include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h"
56
#include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h"
57
#include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsParent.h"
58
#include "mozilla/dom/IPCBlobInputStreamParent.h"
59
#include "mozilla/dom/IPCBlobUtils.h"
60
#include "mozilla/dom/quota/CheckedUnsafePtr.h"
61
#include "mozilla/dom/quota/Client.h"
62
#include "mozilla/dom/quota/FileStreams.h"
63
#include "mozilla/dom/quota/OriginScope.h"
64
#include "mozilla/dom/quota/QuotaCommon.h"
65
#include "mozilla/dom/quota/QuotaManager.h"
66
#include "mozilla/dom/quota/UsageInfo.h"
67
#include "mozilla/ipc/BackgroundParent.h"
68
#include "mozilla/ipc/BackgroundUtils.h"
69
#include "mozilla/ipc/InputStreamParams.h"
70
#include "mozilla/ipc/InputStreamUtils.h"
71
#include "mozilla/ipc/PBackground.h"
72
#include "mozilla/ipc/PBackgroundParent.h"
73
#include "mozilla/Scoped.h"
74
#include "mozilla/storage/Variant.h"
75
#include "mozIStorageFunction.h"
76
#include "mozIStorageProgressHandler.h"
77
#include "mozIStorageService.h"
78
#include "nsAutoPtr.h"
79
#include "nsCharSeparatedTokenizer.h"
80
#include "nsClassHashtable.h"
81
#include "nsCOMPtr.h"
82
#include "nsDataHashtable.h"
83
#include "nsEscape.h"
84
#include "nsExceptionHandler.h"
85
#include "nsHashKeys.h"
86
#include "nsNetUtil.h"
87
#include "nsIAsyncInputStream.h"
88
#include "nsIEventTarget.h"
89
#include "nsIFile.h"
90
#include "nsIFileURL.h"
91
#include "nsIFileProtocolHandler.h"
92
#include "nsIInputStream.h"
93
#include "nsInterfaceHashtable.h"
94
#include "nsIOutputStream.h"
95
#include "nsIPrincipal.h"
96
#include "nsISupports.h"
97
#include "nsISupportsImpl.h"
98
#include "nsISupportsPriority.h"
99
#include "nsIThread.h"
100
#include "nsITimer.h"
101
#include "nsIURI.h"
102
#include "nsIURIMutator.h"
103
#include "nsNetUtil.h"
104
#include "nsPrintfCString.h"
105
#include "nsQueryObject.h"
106
#include "nsRefPtrHashtable.h"
107
#include "nsStreamUtils.h"
108
#include "nsString.h"
109
#include "nsStringStream.h"
110
#include "nsThreadPool.h"
111
#include "nsThreadUtils.h"
112
#include "nsXPCOMCID.h"
113
#include "PermissionRequestBase.h"
114
#include "ProfilerHelpers.h"
115
#include "prsystem.h"
116
#include "prtime.h"
117
#include "ReportInternalError.h"
118
#include "snappy/snappy.h"
119
120
#define DISABLE_ASSERTS_FOR_FUZZING 0
121
122
#if DISABLE_ASSERTS_FOR_FUZZING
123
# define ASSERT_UNLESS_FUZZING(...) \
124
do { \
125
} while (0)
126
#else
127
# define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
128
#endif
129
130
#define IDB_DEBUG_LOG(_args) \
131
MOZ_LOG(IndexedDatabaseManager::GetLoggingModule(), LogLevel::Debug, _args)
132
133
#if defined(MOZ_WIDGET_ANDROID)
134
# define IDB_MOBILE
135
#endif
136
137
namespace mozilla {
138
139
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc,
140
PR_Close);
141
142
namespace dom {
143
namespace indexedDB {
144
145
using namespace mozilla::dom::quota;
146
using namespace mozilla::ipc;
147
using mozilla::dom::quota::Client;
148
149
namespace {
150
151
class ConnectionPool;
152
class Cursor;
153
class Database;
154
struct DatabaseActorInfo;
155
class DatabaseFile;
156
class DatabaseLoggingInfo;
157
class DatabaseMaintenance;
158
class Factory;
159
class Maintenance;
160
class MutableFile;
161
class OpenDatabaseOp;
162
class TransactionBase;
163
class TransactionDatabaseOperationBase;
164
class VersionChangeTransaction;
165
template <bool StatementHasIndexKeyBindings>
166
struct ValueCursorTypeTraits;
167
168
/*******************************************************************************
169
* Constants
170
******************************************************************************/
171
172
// If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
173
// schema version.
174
static_assert(JS_STRUCTURED_CLONE_VERSION == 8,
175
"Need to update the major schema version.");
176
177
// Major schema version. Bump for almost everything.
178
const uint32_t kMajorSchemaVersion = 26;
179
180
// Minor schema version. Should almost always be 0 (maybe bump on release
181
// branches if we have to).
182
const uint32_t kMinorSchemaVersion = 0;
183
184
// The schema version we store in the SQLite database is a (signed) 32-bit
185
// integer. The major version is left-shifted 4 bits so the max value is
186
// 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
187
static_assert(kMajorSchemaVersion <= 0xFFFFFFF,
188
"Major version needs to fit in 28 bits.");
189
static_assert(kMinorSchemaVersion <= 0xF,
190
"Minor version needs to fit in 4 bits.");
191
192
const int32_t kSQLiteSchemaVersion =
193
int32_t((kMajorSchemaVersion << 4) + kMinorSchemaVersion);
194
195
const int32_t kStorageProgressGranularity = 1000;
196
197
// Changing the value here will override the page size of new databases only.
198
// A journal mode change and VACUUM are needed to change existing databases, so
199
// the best way to do that is to use the schema version upgrade mechanism.
200
const uint32_t kSQLitePageSizeOverride =
201
#ifdef IDB_MOBILE
202
2048;
203
#else
204
4096;
205
#endif
206
207
static_assert(kSQLitePageSizeOverride == /* mozStorage default */ 0 ||
208
(kSQLitePageSizeOverride % 2 == 0 &&
209
kSQLitePageSizeOverride >= 512 &&
210
kSQLitePageSizeOverride <= 65536),
211
"Must be 0 (disabled) or a power of 2 between 512 and 65536!");
212
213
// Set to -1 to use SQLite's default, 0 to disable, or some positive number to
214
// enforce a custom limit.
215
const int32_t kMaxWALPages = 5000; // 20MB on desktop, 10MB on mobile.
216
217
// Set to some multiple of the page size to grow the database in larger chunks.
218
const uint32_t kSQLiteGrowthIncrement = kSQLitePageSizeOverride * 2;
219
220
static_assert(kSQLiteGrowthIncrement >= 0 &&
221
kSQLiteGrowthIncrement % kSQLitePageSizeOverride == 0 &&
222
kSQLiteGrowthIncrement < uint32_t(INT32_MAX),
223
"Must be 0 (disabled) or a positive multiple of the page size!");
224
225
// The maximum number of threads that can be used for database activity at a
226
// single time.
227
const uint32_t kMaxConnectionThreadCount = 20;
228
229
static_assert(kMaxConnectionThreadCount, "Must have at least one thread!");
230
231
// The maximum number of threads to keep when idle. Threads that become idle in
232
// excess of this number will be shut down immediately.
233
const uint32_t kMaxIdleConnectionThreadCount = 2;
234
235
static_assert(kMaxConnectionThreadCount >= kMaxIdleConnectionThreadCount,
236
"Idle thread limit must be less than total thread limit!");
237
238
// The length of time that database connections will be held open after all
239
// transactions have completed before doing idle maintenance.
240
const uint32_t kConnectionIdleMaintenanceMS = 2 * 1000; // 2 seconds
241
242
// The length of time that database connections will be held open after all
243
// transactions and maintenance have completed.
244
const uint32_t kConnectionIdleCloseMS = 10 * 1000; // 10 seconds
245
246
// The length of time that idle threads will stay alive before being shut down.
247
const uint32_t kConnectionThreadIdleMS = 30 * 1000; // 30 seconds
248
249
#define SAVEPOINT_CLAUSE "SAVEPOINT sp;"
250
251
const uint32_t kFileCopyBufferSize = 32768;
252
253
constexpr auto kJournalDirectoryName = NS_LITERAL_STRING("journals");
254
255
constexpr auto kFileManagerDirectoryNameSuffix = NS_LITERAL_STRING(".files");
256
constexpr auto kSQLiteSuffix = NS_LITERAL_STRING(".sqlite");
257
constexpr auto kSQLiteJournalSuffix = NS_LITERAL_STRING(".sqlite-journal");
258
constexpr auto kSQLiteSHMSuffix = NS_LITERAL_STRING(".sqlite-shm");
259
constexpr auto kSQLiteWALSuffix = NS_LITERAL_STRING(".sqlite-wal");
260
261
const char kPrefFileHandleEnabled[] = "dom.fileHandle.enabled";
262
263
constexpr auto kPermissionStringBase = NS_LITERAL_CSTRING("indexedDB-chrome-");
264
constexpr auto kPermissionReadSuffix = NS_LITERAL_CSTRING("-read");
265
constexpr auto kPermissionWriteSuffix = NS_LITERAL_CSTRING("-write");
266
267
// The following constants define all names of binding parameters in statements,
268
// where they are bound by name. This should include all parameter names which
269
// are bound by name. Binding may be done by index when the statement definition
270
// and binding are done in the same local scope, and no other reasons prevent
271
// using the indexes (e.g. multiple statement variants with differing number or
272
// order of parameters). Neither the styles of specifying parameter names
273
// (literally vs. via these constants) nor the binding styles (by index vs. by
274
// name) should not be mixed for the same statement. The decision must be made
275
// for each statement based on the proximity of statement and binding calls.
276
constexpr auto kStmtParamNameCurrentKey = NS_LITERAL_CSTRING("current_key");
277
constexpr auto kStmtParamNameRangeBound = NS_LITERAL_CSTRING("range_bound");
278
constexpr auto kStmtParamNameObjectStorePosition =
279
NS_LITERAL_CSTRING("object_store_position");
280
constexpr auto kStmtParamNameLowerKey = NS_LITERAL_CSTRING("lower_key");
281
constexpr auto kStmtParamNameUpperKey = NS_LITERAL_CSTRING("upper_key");
282
constexpr auto kStmtParamNameKey = NS_LITERAL_CSTRING("key");
283
constexpr auto kStmtParamNameObjectStoreId =
284
NS_LITERAL_CSTRING("object_store_id");
285
constexpr auto kStmtParamNameIndexId = NS_LITERAL_CSTRING("index_id");
286
// TODO: Maybe the uses of kStmtParamNameId should be replaced by more
287
// specific constants such as kStmtParamNameObjectStoreId.
288
constexpr auto kStmtParamNameId = NS_LITERAL_CSTRING("id");
289
constexpr auto kStmtParamNameValue = NS_LITERAL_CSTRING("value");
290
constexpr auto kStmtParamNameObjectDataKey =
291
NS_LITERAL_CSTRING("object_data_key");
292
constexpr auto kStmtParamNameIndexDataValues =
293
NS_LITERAL_CSTRING("index_data_values");
294
constexpr auto kStmtParamNameData = NS_LITERAL_CSTRING("data");
295
constexpr auto kStmtParamNameFileIds = NS_LITERAL_CSTRING("file_ids");
296
constexpr auto kStmtParamNameValueLocale = NS_LITERAL_CSTRING("value_locale");
297
constexpr auto kStmtParamNameLimit = NS_LITERAL_CSTRING("limit");
298
299
// The following constants define some names of columns in tables, which are
300
// referred to in remote locations, e.g. in calls to
301
// GetBindingClauseForKeyRange.
302
constexpr auto kColumnNameKey = NS_LITERAL_CSTRING("key");
303
constexpr auto kColumnNameValue = NS_LITERAL_CSTRING("value");
304
constexpr auto kColumnNameAliasSortKey = NS_LITERAL_CSTRING("sort_column");
305
306
// SQL fragments used at multiple locations.
307
constexpr auto kOpenLimit = NS_LITERAL_CSTRING(" LIMIT ");
308
309
// The deletion marker file is created before RemoveDatabaseFilesAndDirectory
310
// begins deleting a database. It is removed as the last step of deletion. If a
311
// deletion marker file is found when initializing the origin, the deletion
312
// routine is run again to ensure that the database and all of its related files
313
// are removed. The primary goal of this mechanism is to avoid situations where
314
// a database has been partially deleted, leading to inconsistent state for the
315
// origin.
316
constexpr auto kIdbDeletionMarkerFilePrefix =
317
NS_LITERAL_STRING("idb-deleting-");
318
319
const uint32_t kDeleteTimeoutMs = 1000;
320
321
/**
322
* Automatically crash the browser if IndexedDB shutdown takes this long. We've
323
* chosen a value that is longer than the value for QuotaManager shutdown timer
324
* which is currently set to 30 seconds. We've also chosen a value that is long
325
* long enough that it is unlikely for the problem to be falsely triggered by
326
* slow system I/O. We've also chosen a value long enough so that automated
327
* tests should time out and fail if IndexedDB shutdown hangs. Also, this value
328
* is long enough so that testers can notice the IndexedDB shutdown hang; we
329
* want to know about the hangs, not hide them. On the other hand this value is
330
* less than 60 seconds which is used by nsTerminator to crash a hung main
331
* process.
332
*/
333
#define SHUTDOWN_TIMEOUT_MS 50000
334
335
#ifdef DEBUG
336
337
const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL;
338
const uint32_t kDEBUGThreadSleepMS = 0;
339
340
const int32_t kDEBUGTransactionThreadPriority =
341
nsISupportsPriority::PRIORITY_NORMAL;
342
const uint32_t kDEBUGTransactionThreadSleepMS = 0;
343
344
#endif
345
346
/*******************************************************************************
347
* Metadata classes
348
******************************************************************************/
349
350
// Can be instantiated either on the QuotaManager IO thread or on a
351
// versionchange transaction thread. These threads can never race so this is
352
// totally safe.
353
struct FullIndexMetadata {
354
IndexMetadata mCommonMetadata = {0, nsString(), KeyPath(0), nsCString(),
355
false, false, false};
356
357
FlippedOnce<false> mDeleted;
358
359
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullIndexMetadata)
360
361
private:
362
~FullIndexMetadata() = default;
363
};
364
365
typedef nsRefPtrHashtable<nsUint64HashKey, FullIndexMetadata> IndexTable;
366
367
// Can be instantiated either on the QuotaManager IO thread or on a
368
// versionchange transaction thread. These threads can never race so this is
369
// totally safe.
370
struct FullObjectStoreMetadata {
371
ObjectStoreMetadata mCommonMetadata = {0, nsString(), KeyPath(0), false};
372
IndexTable mIndexes;
373
374
// These two members are only ever touched on a transaction thread!
375
int64_t mNextAutoIncrementId = 0;
376
int64_t mCommittedAutoIncrementId = 0;
377
378
FlippedOnce<false> mDeleted;
379
380
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullObjectStoreMetadata);
381
382
bool HasLiveIndexes() const;
383
384
private:
385
~FullObjectStoreMetadata() = default;
386
};
387
388
typedef nsRefPtrHashtable<nsUint64HashKey, FullObjectStoreMetadata>
389
ObjectStoreTable;
390
391
struct FullDatabaseMetadata {
392
DatabaseMetadata mCommonMetadata;
393
nsCString mDatabaseId;
394
nsString mFilePath;
395
ObjectStoreTable mObjectStores;
396
397
int64_t mNextObjectStoreId;
398
int64_t mNextIndexId;
399
400
public:
401
explicit FullDatabaseMetadata(const DatabaseMetadata& aCommonMetadata)
402
: mCommonMetadata(aCommonMetadata),
403
mNextObjectStoreId(0),
404
mNextIndexId(0) {
405
AssertIsOnBackgroundThread();
406
}
407
408
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullDatabaseMetadata)
409
410
already_AddRefed<FullDatabaseMetadata> Duplicate() const;
411
412
private:
413
~FullDatabaseMetadata() = default;
414
};
415
416
template <class MetadataType>
417
class MOZ_STACK_CLASS MetadataNameOrIdMatcher final {
418
typedef MetadataNameOrIdMatcher<MetadataType> SelfType;
419
420
const int64_t mId;
421
const nsString mName;
422
RefPtr<MetadataType> mMetadata;
423
bool mCheckName;
424
425
public:
426
template <class Enumerable>
427
static MetadataType* Match(const Enumerable& aEnumerable, uint64_t aId,
428
const nsAString& aName) {
429
AssertIsOnBackgroundThread();
430
MOZ_ASSERT(aId);
431
432
SelfType closure(aId, aName);
433
MatchHelper(aEnumerable, &closure);
434
435
return closure.mMetadata;
436
}
437
438
template <class Enumerable>
439
static MetadataType* Match(const Enumerable& aEnumerable, uint64_t aId) {
440
AssertIsOnBackgroundThread();
441
MOZ_ASSERT(aId);
442
443
SelfType closure(aId);
444
MatchHelper(aEnumerable, &closure);
445
446
return closure.mMetadata;
447
}
448
449
private:
450
MetadataNameOrIdMatcher(const int64_t& aId, const nsAString& aName)
451
: mId(aId), mName(aName), mMetadata(nullptr), mCheckName(true) {
452
AssertIsOnBackgroundThread();
453
MOZ_ASSERT(aId);
454
}
455
456
explicit MetadataNameOrIdMatcher(const int64_t& aId)
457
: mId(aId), mMetadata(nullptr), mCheckName(false) {
458
AssertIsOnBackgroundThread();
459
MOZ_ASSERT(aId);
460
}
461
462
template <class Enumerable>
463
static void MatchHelper(const Enumerable& aEnumerable, SelfType* aClosure) {
464
AssertIsOnBackgroundThread();
465
MOZ_ASSERT(aClosure);
466
467
for (auto iter = aEnumerable.ConstIter(); !iter.Done(); iter.Next()) {
468
#ifdef DEBUG
469
const uint64_t key = iter.Key();
470
#endif
471
MetadataType* value = iter.UserData();
472
MOZ_ASSERT(key != 0);
473
MOZ_ASSERT(value);
474
475
if (!value->mDeleted &&
476
(aClosure->mId == value->mCommonMetadata.id() ||
477
(aClosure->mCheckName &&
478
aClosure->mName == value->mCommonMetadata.name()))) {
479
aClosure->mMetadata = value;
480
break;
481
}
482
}
483
}
484
};
485
486
struct IndexDataValue final {
487
int64_t mIndexId;
488
Key mPosition;
489
Key mLocaleAwarePosition;
490
bool mUnique;
491
492
IndexDataValue() : mIndexId(0), mUnique(false) {
493
MOZ_COUNT_CTOR(IndexDataValue);
494
}
495
496
explicit IndexDataValue(const IndexDataValue& aOther)
497
: mIndexId(aOther.mIndexId),
498
mPosition(aOther.mPosition),
499
mLocaleAwarePosition(aOther.mLocaleAwarePosition),
500
mUnique(aOther.mUnique) {
501
MOZ_ASSERT(!aOther.mPosition.IsUnset());
502
503
MOZ_COUNT_CTOR(IndexDataValue);
504
}
505
506
IndexDataValue(int64_t aIndexId, bool aUnique, const Key& aPosition)
507
: mIndexId(aIndexId), mPosition(aPosition), mUnique(aUnique) {
508
MOZ_ASSERT(!aPosition.IsUnset());
509
510
MOZ_COUNT_CTOR(IndexDataValue);
511
}
512
513
IndexDataValue(int64_t aIndexId, bool aUnique, const Key& aPosition,
514
const Key& aLocaleAwarePosition)
515
: mIndexId(aIndexId),
516
mPosition(aPosition),
517
mLocaleAwarePosition(aLocaleAwarePosition),
518
mUnique(aUnique) {
519
MOZ_ASSERT(!aPosition.IsUnset());
520
521
MOZ_COUNT_CTOR(IndexDataValue);
522
}
523
524
~IndexDataValue() { MOZ_COUNT_DTOR(IndexDataValue); }
525
526
bool operator==(const IndexDataValue& aOther) const {
527
if (mIndexId != aOther.mIndexId) {
528
return false;
529
}
530
if (mLocaleAwarePosition.IsUnset()) {
531
return mPosition == aOther.mPosition;
532
}
533
return mLocaleAwarePosition == aOther.mLocaleAwarePosition;
534
}
535
536
bool operator<(const IndexDataValue& aOther) const {
537
if (mIndexId == aOther.mIndexId) {
538
if (mLocaleAwarePosition.IsUnset()) {
539
return mPosition < aOther.mPosition;
540
}
541
return mLocaleAwarePosition < aOther.mLocaleAwarePosition;
542
}
543
544
return mIndexId < aOther.mIndexId;
545
}
546
};
547
548
/*******************************************************************************
549
* SQLite functions
550
******************************************************************************/
551
552
constexpr int32_t MakeSchemaVersion(uint32_t aMajorSchemaVersion,
553
uint32_t aMinorSchemaVersion) {
554
return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion);
555
}
556
557
// WARNING: the hash function used for the database name must not change.
558
// That's why this function exists separately from mozilla::HashString(), even
559
// though it is (at the time of writing) equivalent. See bug 780408 and bug
560
// 940315 for details.
561
uint32_t HashName(const nsAString& aName) {
562
struct Helper {
563
static uint32_t RotateBitsLeft32(uint32_t aValue, uint8_t aBits) {
564
MOZ_ASSERT(aBits < 32);
565
return (aValue << aBits) | (aValue >> (32 - aBits));
566
}
567
};
568
569
static const uint32_t kGoldenRatioU32 = 0x9e3779b9u;
570
571
return std::accumulate(aName.BeginReading(), aName.EndReading(), uint32_t(0),
572
[](uint32_t hash, char16_t ch) {
573
return kGoldenRatioU32 *
574
(Helper::RotateBitsLeft32(hash, 5) ^ ch);
575
});
576
}
577
578
nsresult ClampResultCode(nsresult aResultCode) {
579
if (NS_SUCCEEDED(aResultCode) ||
580
NS_ERROR_GET_MODULE(aResultCode) == NS_ERROR_MODULE_DOM_INDEXEDDB) {
581
return aResultCode;
582
}
583
584
switch (aResultCode) {
585
case NS_ERROR_FILE_NO_DEVICE_SPACE:
586
return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
587
case NS_ERROR_STORAGE_CONSTRAINT:
588
return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
589
default:
590
#ifdef DEBUG
591
nsPrintfCString message("Converting non-IndexedDB error code (0x%" PRIX32
592
") to "
593
"NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR",
594
static_cast<uint32_t>(aResultCode));
595
NS_WARNING(message.get());
596
#else
597
;
598
#endif
599
}
600
601
IDB_REPORT_INTERNAL_ERR();
602
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
603
}
604
605
void GetDatabaseFilename(const nsAString& aName,
606
nsAutoString& aDatabaseFilename) {
607
MOZ_ASSERT(aDatabaseFilename.IsEmpty());
608
609
// WARNING: do not change this hash function. See the comment in HashName()
610
// for details.
611
aDatabaseFilename.AppendInt(HashName(aName));
612
613
nsCString escapedName;
614
if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) {
615
MOZ_CRASH("Can't escape database name!");
616
}
617
618
const char* forwardIter = escapedName.BeginReading();
619
const char* backwardIter = escapedName.EndReading() - 1;
620
621
nsAutoCString substring;
622
while (forwardIter <= backwardIter && substring.Length() < 21) {
623
if (substring.Length() % 2) {
624
substring.Append(*backwardIter--);
625
} else {
626
substring.Append(*forwardIter++);
627
}
628
}
629
630
aDatabaseFilename.AppendASCII(substring.get(), substring.Length());
631
}
632
633
uint32_t CompressedByteCountForNumber(uint64_t aNumber) {
634
// All bytes have 7 bits available.
635
uint32_t count = 1;
636
while ((aNumber >>= 7)) {
637
count++;
638
}
639
640
return count;
641
}
642
643
uint32_t CompressedByteCountForIndexId(int64_t aIndexId) {
644
MOZ_ASSERT(aIndexId);
645
MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId),
646
"Overflow!");
647
648
return CompressedByteCountForNumber(uint64_t(aIndexId * 2));
649
}
650
651
void WriteCompressedNumber(uint64_t aNumber, uint8_t** aIterator) {
652
MOZ_ASSERT(aIterator);
653
MOZ_ASSERT(*aIterator);
654
655
uint8_t*& buffer = *aIterator;
656
657
#ifdef DEBUG
658
const uint8_t* const bufferStart = buffer;
659
const uint64_t originalNumber = aNumber;
660
#endif
661
662
while (true) {
663
uint64_t shiftedNumber = aNumber >> 7;
664
if (shiftedNumber) {
665
*buffer++ = uint8_t(0x80 | (aNumber & 0x7f));
666
aNumber = shiftedNumber;
667
} else {
668
*buffer++ = uint8_t(aNumber);
669
break;
670
}
671
}
672
673
MOZ_ASSERT(buffer > bufferStart);
674
MOZ_ASSERT(uint32_t(buffer - bufferStart) ==
675
CompressedByteCountForNumber(originalNumber));
676
}
677
678
uint64_t ReadCompressedNumber(const uint8_t** aIterator, const uint8_t* aEnd) {
679
MOZ_ASSERT(aIterator);
680
MOZ_ASSERT(*aIterator);
681
MOZ_ASSERT(aEnd);
682
MOZ_ASSERT(*aIterator < aEnd);
683
684
const uint8_t*& buffer = *aIterator;
685
686
uint8_t shiftCounter = 0;
687
uint64_t result = 0;
688
689
while (true) {
690
MOZ_ASSERT(shiftCounter <= 56, "Shifted too many bits!");
691
692
result += (uint64_t(*buffer & 0x7f) << shiftCounter);
693
shiftCounter += 7;
694
695
if (!(*buffer++ & 0x80)) {
696
break;
697
}
698
699
if (NS_WARN_IF(buffer == aEnd)) {
700
MOZ_ASSERT(false);
701
break;
702
}
703
}
704
705
return result;
706
}
707
708
void WriteCompressedIndexId(int64_t aIndexId, bool aUnique,
709
uint8_t** aIterator) {
710
MOZ_ASSERT(aIndexId);
711
MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId),
712
"Overflow!");
713
MOZ_ASSERT(aIterator);
714
MOZ_ASSERT(*aIterator);
715
716
const uint64_t indexId = (uint64_t(aIndexId * 2) | (aUnique ? 1 : 0));
717
WriteCompressedNumber(indexId, aIterator);
718
}
719
720
void ReadCompressedIndexId(const uint8_t** aIterator, const uint8_t* aEnd,
721
int64_t* aIndexId, bool* aUnique) {
722
MOZ_ASSERT(aIterator);
723
MOZ_ASSERT(*aIterator);
724
MOZ_ASSERT(aIndexId);
725
MOZ_ASSERT(aUnique);
726
727
uint64_t indexId = ReadCompressedNumber(aIterator, aEnd);
728
729
if (indexId % 2) {
730
*aUnique = true;
731
indexId--;
732
} else {
733
*aUnique = false;
734
}
735
736
MOZ_ASSERT(UINT64_MAX / 2 >= uint64_t(indexId), "Bad index id!");
737
738
*aIndexId = int64_t(indexId / 2);
739
}
740
741
// static
742
nsresult MakeCompressedIndexDataValues(
743
const FallibleTArray<IndexDataValue>& aIndexValues,
744
UniqueFreePtr<uint8_t>& aCompressedIndexDataValues,
745
uint32_t* aCompressedIndexDataValuesLength) {
746
MOZ_ASSERT(!NS_IsMainThread());
747
MOZ_ASSERT(!IsOnBackgroundThread());
748
MOZ_ASSERT(!aCompressedIndexDataValues);
749
MOZ_ASSERT(aCompressedIndexDataValuesLength);
750
751
AUTO_PROFILER_LABEL("MakeCompressedIndexDataValues", DOM);
752
753
const uint32_t arrayLength = aIndexValues.Length();
754
if (!arrayLength) {
755
*aCompressedIndexDataValuesLength = 0;
756
return NS_OK;
757
}
758
759
// First calculate the size of the final buffer.
760
uint32_t blobDataLength = 0;
761
762
for (const IndexDataValue& info : aIndexValues) {
763
const nsCString& keyBuffer = info.mPosition.GetBuffer();
764
const nsCString& sortKeyBuffer = info.mLocaleAwarePosition.GetBuffer();
765
const uint32_t keyBufferLength = keyBuffer.Length();
766
const uint32_t sortKeyBufferLength = sortKeyBuffer.Length();
767
768
MOZ_ASSERT(!keyBuffer.IsEmpty());
769
770
const CheckedUint32 infoLength =
771
CheckedUint32(CompressedByteCountForIndexId(info.mIndexId)) +
772
CompressedByteCountForNumber(keyBufferLength) +
773
CompressedByteCountForNumber(sortKeyBufferLength) + keyBufferLength +
774
sortKeyBufferLength;
775
// Don't let |infoLength| overflow.
776
if (NS_WARN_IF(!infoLength.isValid())) {
777
IDB_REPORT_INTERNAL_ERR();
778
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
779
}
780
781
// Don't let |blobDataLength| overflow.
782
if (NS_WARN_IF(UINT32_MAX - infoLength.value() < blobDataLength)) {
783
IDB_REPORT_INTERNAL_ERR();
784
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
785
}
786
787
blobDataLength += infoLength.value();
788
}
789
790
UniqueFreePtr<uint8_t> blobData(
791
static_cast<uint8_t*>(malloc(blobDataLength)));
792
if (NS_WARN_IF(!blobData)) {
793
IDB_REPORT_INTERNAL_ERR();
794
return NS_ERROR_OUT_OF_MEMORY;
795
}
796
797
uint8_t* blobDataIter = blobData.get();
798
799
for (const IndexDataValue& info : aIndexValues) {
800
const nsCString& keyBuffer = info.mPosition.GetBuffer();
801
const nsCString& sortKeyBuffer = info.mLocaleAwarePosition.GetBuffer();
802
const uint32_t keyBufferLength = keyBuffer.Length();
803
const uint32_t sortKeyBufferLength = sortKeyBuffer.Length();
804
805
WriteCompressedIndexId(info.mIndexId, info.mUnique, &blobDataIter);
806
WriteCompressedNumber(keyBufferLength, &blobDataIter);
807
808
memcpy(blobDataIter, keyBuffer.get(), keyBufferLength);
809
blobDataIter += keyBufferLength;
810
811
WriteCompressedNumber(sortKeyBufferLength, &blobDataIter);
812
813
memcpy(blobDataIter, sortKeyBuffer.get(), sortKeyBufferLength);
814
blobDataIter += sortKeyBufferLength;
815
}
816
817
MOZ_ASSERT(blobDataIter == blobData.get() + blobDataLength);
818
819
aCompressedIndexDataValues.swap(blobData);
820
*aCompressedIndexDataValuesLength = uint32_t(blobDataLength);
821
822
return NS_OK;
823
}
824
825
nsresult ReadCompressedIndexDataValuesFromBlob(
826
const uint8_t* aBlobData, uint32_t aBlobDataLength,
827
nsTArray<IndexDataValue>& aIndexValues) {
828
MOZ_ASSERT(!NS_IsMainThread());
829
MOZ_ASSERT(!IsOnBackgroundThread());
830
MOZ_ASSERT(aBlobData);
831
MOZ_ASSERT(aBlobDataLength);
832
MOZ_ASSERT(aIndexValues.IsEmpty());
833
834
AUTO_PROFILER_LABEL("ReadCompressedIndexDataValuesFromBlob", DOM);
835
836
if (uintptr_t(aBlobData) > UINTPTR_MAX - aBlobDataLength) {
837
IDB_REPORT_INTERNAL_ERR();
838
return NS_ERROR_FILE_CORRUPTED;
839
}
840
841
const uint8_t* blobDataIter = aBlobData;
842
const uint8_t* const blobDataEnd = aBlobData + aBlobDataLength;
843
844
while (blobDataIter < blobDataEnd) {
845
int64_t indexId;
846
bool unique;
847
ReadCompressedIndexId(&blobDataIter, blobDataEnd, &indexId, &unique);
848
849
if (NS_WARN_IF(blobDataIter == blobDataEnd)) {
850
IDB_REPORT_INTERNAL_ERR();
851
return NS_ERROR_FILE_CORRUPTED;
852
}
853
854
// Read key buffer length.
855
const uint64_t keyBufferLength =
856
ReadCompressedNumber(&blobDataIter, blobDataEnd);
857
858
if (NS_WARN_IF(blobDataIter == blobDataEnd) ||
859
NS_WARN_IF(keyBufferLength > uint64_t(UINT32_MAX)) ||
860
NS_WARN_IF(keyBufferLength > uintptr_t(blobDataEnd)) ||
861
NS_WARN_IF(blobDataIter > blobDataEnd - keyBufferLength)) {
862
IDB_REPORT_INTERNAL_ERR();
863
return NS_ERROR_FILE_CORRUPTED;
864
}
865
866
nsCString keyBuffer(reinterpret_cast<const char*>(blobDataIter),
867
uint32_t(keyBufferLength));
868
blobDataIter += keyBufferLength;
869
870
IndexDataValue idv(indexId, unique, Key(keyBuffer));
871
872
// Read sort key buffer length.
873
const uint64_t sortKeyBufferLength =
874
ReadCompressedNumber(&blobDataIter, blobDataEnd);
875
876
if (sortKeyBufferLength > 0) {
877
if (NS_WARN_IF(blobDataIter == blobDataEnd) ||
878
NS_WARN_IF(sortKeyBufferLength > uint64_t(UINT32_MAX)) ||
879
NS_WARN_IF(sortKeyBufferLength > uintptr_t(blobDataEnd)) ||
880
NS_WARN_IF(blobDataIter > blobDataEnd - sortKeyBufferLength)) {
881
IDB_REPORT_INTERNAL_ERR();
882
return NS_ERROR_FILE_CORRUPTED;
883
}
884
885
nsCString sortKeyBuffer(reinterpret_cast<const char*>(blobDataIter),
886
uint32_t(sortKeyBufferLength));
887
blobDataIter += sortKeyBufferLength;
888
889
idv.mLocaleAwarePosition = Key(sortKeyBuffer);
890
}
891
892
if (NS_WARN_IF(!aIndexValues.InsertElementSorted(idv, fallible))) {
893
IDB_REPORT_INTERNAL_ERR();
894
return NS_ERROR_OUT_OF_MEMORY;
895
}
896
}
897
898
MOZ_ASSERT(blobDataIter == blobDataEnd);
899
900
return NS_OK;
901
}
902
903
// static
904
template <typename T>
905
nsresult ReadCompressedIndexDataValuesFromSource(
906
T* aSource, uint32_t aColumnIndex, nsTArray<IndexDataValue>& aIndexValues) {
907
MOZ_ASSERT(!NS_IsMainThread());
908
MOZ_ASSERT(!IsOnBackgroundThread());
909
MOZ_ASSERT(aSource);
910
MOZ_ASSERT(aIndexValues.IsEmpty());
911
912
int32_t columnType;
913
nsresult rv = aSource->GetTypeOfIndex(aColumnIndex, &columnType);
914
if (NS_WARN_IF(NS_FAILED(rv))) {
915
return rv;
916
}
917
918
if (columnType == mozIStorageStatement::VALUE_TYPE_NULL) {
919
return NS_OK;
920
}
921
922
MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_BLOB);
923
924
const uint8_t* blobData;
925
uint32_t blobDataLength;
926
rv = aSource->GetSharedBlob(aColumnIndex, &blobDataLength, &blobData);
927
if (NS_WARN_IF(NS_FAILED(rv))) {
928
return rv;
929
}
930
931
if (NS_WARN_IF(!blobDataLength)) {
932
IDB_REPORT_INTERNAL_ERR();
933
return NS_ERROR_FILE_CORRUPTED;
934
}
935
936
rv = ReadCompressedIndexDataValuesFromBlob(blobData, blobDataLength,
937
aIndexValues);
938
if (NS_WARN_IF(NS_FAILED(rv))) {
939
return rv;
940
}
941
942
return NS_OK;
943
}
944
945
nsresult ReadCompressedIndexDataValues(mozIStorageStatement* aStatement,
946
uint32_t aColumnIndex,
947
nsTArray<IndexDataValue>& aIndexValues) {
948
return ReadCompressedIndexDataValuesFromSource(aStatement, aColumnIndex,
949
aIndexValues);
950
}
951
952
nsresult ReadCompressedIndexDataValues(mozIStorageValueArray* aValues,
953
uint32_t aColumnIndex,
954
nsTArray<IndexDataValue>& aIndexValues) {
955
return ReadCompressedIndexDataValuesFromSource(aValues, aColumnIndex,
956
aIndexValues);
957
}
958
959
nsresult CreateFileTables(mozIStorageConnection* aConnection) {
960
AssertIsOnIOThread();
961
MOZ_ASSERT(aConnection);
962
963
AUTO_PROFILER_LABEL("CreateFileTables", DOM);
964
965
// Table `file`
966
nsresult rv = aConnection->ExecuteSimpleSQL(
967
NS_LITERAL_CSTRING("CREATE TABLE file ("
968
"id INTEGER PRIMARY KEY, "
969
"refcount INTEGER NOT NULL"
970
");"));
971
if (NS_WARN_IF(NS_FAILED(rv))) {
972
return rv;
973
}
974
975
rv = aConnection->ExecuteSimpleSQL(
976
NS_LITERAL_CSTRING("CREATE TRIGGER object_data_insert_trigger "
977
"AFTER INSERT ON object_data "
978
"FOR EACH ROW "
979
"WHEN NEW.file_ids IS NOT NULL "
980
"BEGIN "
981
"SELECT update_refcount(NULL, NEW.file_ids); "
982
"END;"));
983
if (NS_WARN_IF(NS_FAILED(rv))) {
984
return rv;
985
}
986
987
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
988
"CREATE TRIGGER object_data_update_trigger "
989
"AFTER UPDATE OF file_ids ON object_data "
990
"FOR EACH ROW "
991
"WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
992
"BEGIN "
993
"SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
994
"END;"));
995
if (NS_WARN_IF(NS_FAILED(rv))) {
996
return rv;
997
}
998
999
rv = aConnection->ExecuteSimpleSQL(
1000
NS_LITERAL_CSTRING("CREATE TRIGGER object_data_delete_trigger "
1001
"AFTER DELETE ON object_data "
1002
"FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
1003
"BEGIN "
1004
"SELECT update_refcount(OLD.file_ids, NULL); "
1005
"END;"));
1006
if (NS_WARN_IF(NS_FAILED(rv))) {
1007
return rv;
1008
}
1009
1010
rv = aConnection->ExecuteSimpleSQL(
1011
NS_LITERAL_CSTRING("CREATE TRIGGER file_update_trigger "
1012
"AFTER UPDATE ON file "
1013
"FOR EACH ROW WHEN NEW.refcount = 0 "
1014
"BEGIN "
1015
"DELETE FROM file WHERE id = OLD.id; "
1016
"END;"));
1017
if (NS_WARN_IF(NS_FAILED(rv))) {
1018
return rv;
1019
}
1020
1021
return NS_OK;
1022
}
1023
1024
nsresult CreateTables(mozIStorageConnection* aConnection) {
1025
AssertIsOnIOThread();
1026
MOZ_ASSERT(aConnection);
1027
1028
AUTO_PROFILER_LABEL("CreateTables", DOM);
1029
1030
// Table `database`
1031
1032
// There are two reasons for having the origin column.
1033
// First, we can ensure that we don't have collisions in the origin hash we
1034
// use for the path because when we open the db we can make sure that the
1035
// origins exactly match. Second, chrome code crawling through the idb
1036
// directory can figure out the origin of every db without having to
1037
// reverse-engineer our hash scheme.
1038
nsresult rv = aConnection->ExecuteSimpleSQL(
1039
NS_LITERAL_CSTRING("CREATE TABLE database"
1040
"( name TEXT PRIMARY KEY"
1041
", origin TEXT NOT NULL"
1042
", version INTEGER NOT NULL DEFAULT 0"
1043
", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
1044
", last_analyze_time INTEGER NOT NULL DEFAULT 0"
1045
", last_vacuum_size INTEGER NOT NULL DEFAULT 0"
1046
") WITHOUT ROWID;"));
1047
if (NS_WARN_IF(NS_FAILED(rv))) {
1048
return rv;
1049
}
1050
1051
// Table `object_store`
1052
rv = aConnection->ExecuteSimpleSQL(
1053
NS_LITERAL_CSTRING("CREATE TABLE object_store"
1054
"( id INTEGER PRIMARY KEY"
1055
", auto_increment INTEGER NOT NULL DEFAULT 0"
1056
", name TEXT NOT NULL"
1057
", key_path TEXT"
1058
");"));
1059
if (NS_WARN_IF(NS_FAILED(rv))) {
1060
return rv;
1061
}
1062
1063
// Table `object_store_index`
1064
rv = aConnection->ExecuteSimpleSQL(
1065
NS_LITERAL_CSTRING("CREATE TABLE object_store_index"
1066
"( id INTEGER PRIMARY KEY"
1067
", object_store_id INTEGER NOT NULL"
1068
", name TEXT NOT NULL"
1069
", key_path TEXT NOT NULL"
1070
", unique_index INTEGER NOT NULL"
1071
", multientry INTEGER NOT NULL"
1072
", locale TEXT"
1073
", is_auto_locale BOOLEAN NOT NULL"
1074
", FOREIGN KEY (object_store_id) "
1075
"REFERENCES object_store(id) "
1076
");"));
1077
if (NS_WARN_IF(NS_FAILED(rv))) {
1078
return rv;
1079
}
1080
1081
// Table `object_data`
1082
rv = aConnection->ExecuteSimpleSQL(
1083
NS_LITERAL_CSTRING("CREATE TABLE object_data"
1084
"( object_store_id INTEGER NOT NULL"
1085
", key BLOB NOT NULL"
1086
", index_data_values BLOB DEFAULT NULL"
1087
", file_ids TEXT"
1088
", data BLOB NOT NULL"
1089
", PRIMARY KEY (object_store_id, key)"
1090
", FOREIGN KEY (object_store_id) "
1091
"REFERENCES object_store(id) "
1092
") WITHOUT ROWID;"));
1093
if (NS_WARN_IF(NS_FAILED(rv))) {
1094
return rv;
1095
}
1096
1097
// Table `index_data`
1098
rv = aConnection->ExecuteSimpleSQL(
1099
NS_LITERAL_CSTRING("CREATE TABLE index_data"
1100
"( index_id INTEGER NOT NULL"
1101
", value BLOB NOT NULL"
1102
", object_data_key BLOB NOT NULL"
1103
", object_store_id INTEGER NOT NULL"
1104
", value_locale BLOB"
1105
", PRIMARY KEY (index_id, value, object_data_key)"
1106
", FOREIGN KEY (index_id) "
1107
"REFERENCES object_store_index(id) "
1108
", FOREIGN KEY (object_store_id, object_data_key) "
1109
"REFERENCES object_data(object_store_id, key) "
1110
") WITHOUT ROWID;"));
1111
if (NS_WARN_IF(NS_FAILED(rv))) {
1112
return rv;
1113
}
1114
1115
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1116
"CREATE INDEX index_data_value_locale_index "
1117
"ON index_data (index_id, value_locale, object_data_key, value) "
1118
"WHERE value_locale IS NOT NULL;"));
1119
if (NS_WARN_IF(NS_FAILED(rv))) {
1120
return rv;
1121
}
1122
1123
// Table `unique_index_data`
1124
rv = aConnection->ExecuteSimpleSQL(
1125
NS_LITERAL_CSTRING("CREATE TABLE unique_index_data"
1126
"( index_id INTEGER NOT NULL"
1127
", value BLOB NOT NULL"
1128
", object_store_id INTEGER NOT NULL"
1129
", object_data_key BLOB NOT NULL"
1130
", value_locale BLOB"
1131
", PRIMARY KEY (index_id, value)"
1132
", FOREIGN KEY (index_id) "
1133
"REFERENCES object_store_index(id) "
1134
", FOREIGN KEY (object_store_id, object_data_key) "
1135
"REFERENCES object_data(object_store_id, key) "
1136
") WITHOUT ROWID;"));
1137
if (NS_WARN_IF(NS_FAILED(rv))) {
1138
return rv;
1139
}
1140
1141
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1142
"CREATE INDEX unique_index_data_value_locale_index "
1143
"ON unique_index_data (index_id, value_locale, object_data_key, value) "
1144
"WHERE value_locale IS NOT NULL;"));
1145
if (NS_WARN_IF(NS_FAILED(rv))) {
1146
return rv;
1147
}
1148
1149
rv = CreateFileTables(aConnection);
1150
if (NS_WARN_IF(NS_FAILED(rv))) {
1151
return rv;
1152
}
1153
1154
rv = aConnection->SetSchemaVersion(kSQLiteSchemaVersion);
1155
if (NS_WARN_IF(NS_FAILED(rv))) {
1156
return rv;
1157
}
1158
1159
return NS_OK;
1160
}
1161
1162
nsresult UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection) {
1163
AssertIsOnIOThread();
1164
MOZ_ASSERT(aConnection);
1165
1166
AUTO_PROFILER_LABEL("UpgradeSchemaFrom4To5", DOM);
1167
1168
nsresult rv;
1169
1170
// All we changed is the type of the version column, so lets try to
1171
// convert that to an integer, and if we fail, set it to 0.
1172
nsCOMPtr<mozIStorageStatement> stmt;
1173
rv = aConnection->CreateStatement(
1174
NS_LITERAL_CSTRING("SELECT name, version, dataVersion "
1175
"FROM database"),
1176
getter_AddRefs(stmt));
1177
if (NS_WARN_IF(NS_FAILED(rv))) {
1178
return rv;
1179
}
1180
1181
nsString name;
1182
int32_t intVersion;
1183
int64_t dataVersion;
1184
1185
{
1186
mozStorageStatementScoper scoper(stmt);
1187
1188
bool hasResults;
1189
rv = stmt->ExecuteStep(&hasResults);
1190
if (NS_WARN_IF(NS_FAILED(rv))) {
1191
return rv;
1192
}
1193
if (NS_WARN_IF(!hasResults)) {
1194
return NS_ERROR_FAILURE;
1195
}
1196
1197
nsString version;
1198
rv = stmt->GetString(1, version);
1199
if (NS_WARN_IF(NS_FAILED(rv))) {
1200
return rv;
1201
}
1202
1203
intVersion = version.ToInteger(&rv);
1204
if (NS_FAILED(rv)) {
1205
intVersion = 0;
1206
}
1207
1208
rv = stmt->GetString(0, name);
1209
if (NS_WARN_IF(NS_FAILED(rv))) {
1210
return rv;
1211
}
1212
1213
rv = stmt->GetInt64(2, &dataVersion);
1214
if (NS_WARN_IF(NS_FAILED(rv))) {
1215
return rv;
1216
}
1217
}
1218
1219
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE database"));
1220
if (NS_WARN_IF(NS_FAILED(rv))) {
1221
return rv;
1222
}
1223
1224
rv = aConnection->ExecuteSimpleSQL(
1225
NS_LITERAL_CSTRING("CREATE TABLE database ("
1226
"name TEXT NOT NULL, "
1227
"version INTEGER NOT NULL DEFAULT 0, "
1228
"dataVersion INTEGER NOT NULL"
1229
");"));
1230
if (NS_WARN_IF(NS_FAILED(rv))) {
1231
return rv;
1232
}
1233
1234
// The parameter names are not used, parameters are bound by index only
1235
// locally in the same function.
1236
rv = aConnection->CreateStatement(
1237
NS_LITERAL_CSTRING("INSERT INTO database (name, version, dataVersion) "
1238
"VALUES (:name, :version, :dataVersion)"),
1239
getter_AddRefs(stmt));
1240
if (NS_WARN_IF(NS_FAILED(rv))) {
1241
return rv;
1242
}
1243
1244
{
1245
mozStorageStatementScoper scoper(stmt);
1246
1247
rv = stmt->BindStringByIndex(0, name);
1248
if (NS_WARN_IF(NS_FAILED(rv))) {
1249
return rv;
1250
}
1251
1252
rv = stmt->BindInt32ByIndex(1, intVersion);
1253
if (NS_WARN_IF(NS_FAILED(rv))) {
1254
return rv;
1255
}
1256
1257
rv = stmt->BindInt64ByIndex(2, dataVersion);
1258
if (NS_WARN_IF(NS_FAILED(rv))) {
1259
return rv;
1260
}
1261
1262
rv = stmt->Execute();
1263
if (NS_WARN_IF(NS_FAILED(rv))) {
1264
return rv;
1265
}
1266
}
1267
1268
rv = aConnection->SetSchemaVersion(5);
1269
if (NS_WARN_IF(NS_FAILED(rv))) {
1270
return rv;
1271
}
1272
1273
return NS_OK;
1274
}
1275
1276
nsresult UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection) {
1277
AssertIsOnIOThread();
1278
MOZ_ASSERT(aConnection);
1279
1280
AUTO_PROFILER_LABEL("UpgradeSchemaFrom5To6", DOM);
1281
1282
// First, drop all the indexes we're no longer going to use.
1283
nsresult rv = aConnection->ExecuteSimpleSQL(
1284
NS_LITERAL_CSTRING("DROP INDEX key_index;"));
1285
if (NS_WARN_IF(NS_FAILED(rv))) {
1286
return rv;
1287
}
1288
1289
rv = aConnection->ExecuteSimpleSQL(
1290
NS_LITERAL_CSTRING("DROP INDEX ai_key_index;"));
1291
if (NS_WARN_IF(NS_FAILED(rv))) {
1292
return rv;
1293
}
1294
1295
rv = aConnection->ExecuteSimpleSQL(
1296
NS_LITERAL_CSTRING("DROP INDEX value_index;"));
1297
if (NS_WARN_IF(NS_FAILED(rv))) {
1298
return rv;
1299
}
1300
1301
rv = aConnection->ExecuteSimpleSQL(
1302
NS_LITERAL_CSTRING("DROP INDEX ai_value_index;"));
1303
if (NS_WARN_IF(NS_FAILED(rv))) {
1304
return rv;
1305
}
1306
1307
// Now, reorder the columns of object_data to put the blob data last. We do
1308
// this by copying into a temporary table, dropping the original, then copying
1309
// back into a newly created table.
1310
rv = aConnection->ExecuteSimpleSQL(
1311
NS_LITERAL_CSTRING("CREATE TEMPORARY TABLE temp_upgrade ("
1312
"id INTEGER PRIMARY KEY, "
1313
"object_store_id, "
1314
"key_value, "
1315
"data "
1316
");"));
1317
if (NS_WARN_IF(NS_FAILED(rv))) {
1318
return rv;
1319
}
1320
1321
rv = aConnection->ExecuteSimpleSQL(
1322
NS_LITERAL_CSTRING("INSERT INTO temp_upgrade "
1323
"SELECT id, object_store_id, key_value, data "
1324
"FROM object_data;"));
1325
if (NS_WARN_IF(NS_FAILED(rv))) {
1326
return rv;
1327
}
1328
1329
rv = aConnection->ExecuteSimpleSQL(
1330
NS_LITERAL_CSTRING("DROP TABLE object_data;"));
1331
if (NS_WARN_IF(NS_FAILED(rv))) {
1332
return rv;
1333
}
1334
1335
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1336
"CREATE TABLE object_data ("
1337
"id INTEGER PRIMARY KEY, "
1338
"object_store_id INTEGER NOT NULL, "
1339
"key_value DEFAULT NULL, "
1340
"data BLOB NOT NULL, "
1341
"UNIQUE (object_store_id, key_value), "
1342
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1343
"CASCADE"
1344
");"));
1345
if (NS_WARN_IF(NS_FAILED(rv))) {
1346
return rv;
1347
}
1348
1349
rv = aConnection->ExecuteSimpleSQL(
1350
NS_LITERAL_CSTRING("INSERT INTO object_data "
1351
"SELECT id, object_store_id, key_value, data "
1352
"FROM temp_upgrade;"));
1353
if (NS_WARN_IF(NS_FAILED(rv))) {
1354
return rv;
1355
}
1356
1357
rv = aConnection->ExecuteSimpleSQL(
1358
NS_LITERAL_CSTRING("DROP TABLE temp_upgrade;"));
1359
if (NS_WARN_IF(NS_FAILED(rv))) {
1360
return rv;
1361
}
1362
1363
// We need to add a unique constraint to our ai_object_data table. Copy all
1364
// the data out of it using a temporary table as before.
1365
rv = aConnection->ExecuteSimpleSQL(
1366
NS_LITERAL_CSTRING("CREATE TEMPORARY TABLE temp_upgrade ("
1367
"id INTEGER PRIMARY KEY, "
1368
"object_store_id, "
1369
"data "
1370
");"));
1371
if (NS_WARN_IF(NS_FAILED(rv))) {
1372
return rv;
1373
}
1374
1375
rv = aConnection->ExecuteSimpleSQL(
1376
NS_LITERAL_CSTRING("INSERT INTO temp_upgrade "
1377
"SELECT id, object_store_id, data "
1378
"FROM ai_object_data;"));
1379
if (NS_WARN_IF(NS_FAILED(rv))) {
1380
return rv;
1381
}
1382
1383
rv = aConnection->ExecuteSimpleSQL(
1384
NS_LITERAL_CSTRING("DROP TABLE ai_object_data;"));
1385
if (NS_WARN_IF(NS_FAILED(rv))) {
1386
return rv;
1387
}
1388
1389
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1390
"CREATE TABLE ai_object_data ("
1391
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
1392
"object_store_id INTEGER NOT NULL, "
1393
"data BLOB NOT NULL, "
1394
"UNIQUE (object_store_id, id), "
1395
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1396
"CASCADE"
1397
");"));
1398
if (NS_WARN_IF(NS_FAILED(rv))) {
1399
return rv;
1400
}
1401
1402
rv = aConnection->ExecuteSimpleSQL(
1403
NS_LITERAL_CSTRING("INSERT INTO ai_object_data "
1404
"SELECT id, object_store_id, data "
1405
"FROM temp_upgrade;"));
1406
if (NS_WARN_IF(NS_FAILED(rv))) {
1407
return rv;
1408
}
1409
1410
rv = aConnection->ExecuteSimpleSQL(
1411
NS_LITERAL_CSTRING("DROP TABLE temp_upgrade;"));
1412
if (NS_WARN_IF(NS_FAILED(rv))) {
1413
return rv;
1414
}
1415
1416
// Fix up the index_data table. We're reordering the columns as well as
1417
// changing the primary key from being a simple id to being a composite.
1418
rv = aConnection->ExecuteSimpleSQL(
1419
NS_LITERAL_CSTRING("CREATE TEMPORARY TABLE temp_upgrade ("
1420
"index_id, "
1421
"value, "
1422
"object_data_key, "
1423
"object_data_id "
1424
");"));
1425
if (NS_WARN_IF(NS_FAILED(rv))) {
1426
return rv;
1427
}
1428
1429
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1430
"INSERT INTO temp_upgrade "
1431
"SELECT index_id, value, object_data_key, object_data_id "
1432
"FROM index_data;"));
1433
if (NS_WARN_IF(NS_FAILED(rv))) {
1434
return rv;
1435
}
1436
1437
rv = aConnection->ExecuteSimpleSQL(
1438
NS_LITERAL_CSTRING("DROP TABLE index_data;"));
1439
if (NS_WARN_IF(NS_FAILED(rv))) {
1440
return rv;
1441
}
1442
1443
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1444
"CREATE TABLE index_data ("
1445
"index_id INTEGER NOT NULL, "
1446
"value NOT NULL, "
1447
"object_data_key NOT NULL, "
1448
"object_data_id INTEGER NOT NULL, "
1449
"PRIMARY KEY (index_id, value, object_data_key), "
1450
"FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1451
"CASCADE, "
1452
"FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1453
"CASCADE"
1454
");"));
1455
if (NS_WARN_IF(NS_FAILED(rv))) {
1456
return rv;
1457
}
1458
1459
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1460
"INSERT OR IGNORE INTO index_data "
1461
"SELECT index_id, value, object_data_key, object_data_id "
1462
"FROM temp_upgrade;"));
1463
if (NS_WARN_IF(NS_FAILED(rv))) {
1464
return rv;
1465
}
1466
1467
rv = aConnection->ExecuteSimpleSQL(
1468
NS_LITERAL_CSTRING("DROP TABLE temp_upgrade;"));
1469
if (NS_WARN_IF(NS_FAILED(rv))) {
1470
return rv;
1471
}
1472
1473
rv = aConnection->ExecuteSimpleSQL(
1474
NS_LITERAL_CSTRING("CREATE INDEX index_data_object_data_id_index "
1475
"ON index_data (object_data_id);"));
1476
if (NS_WARN_IF(NS_FAILED(rv))) {
1477
return rv;
1478
}
1479
1480
// Fix up the unique_index_data table. We're reordering the columns as well as
1481
// changing the primary key from being a simple id to being a composite.
1482
rv = aConnection->ExecuteSimpleSQL(
1483
NS_LITERAL_CSTRING("CREATE TEMPORARY TABLE temp_upgrade ("
1484
"index_id, "
1485
"value, "
1486
"object_data_key, "
1487
"object_data_id "
1488
");"));
1489
if (NS_WARN_IF(NS_FAILED(rv))) {
1490
return rv;
1491
}
1492
1493
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1494
"INSERT INTO temp_upgrade "
1495
"SELECT index_id, value, object_data_key, object_data_id "
1496
"FROM unique_index_data;"));
1497
if (NS_WARN_IF(NS_FAILED(rv))) {
1498
return rv;
1499
}
1500
1501
rv = aConnection->ExecuteSimpleSQL(
1502
NS_LITERAL_CSTRING("DROP TABLE unique_index_data;"));
1503
if (NS_WARN_IF(NS_FAILED(rv))) {
1504
return rv;
1505
}
1506
1507
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1508
"CREATE TABLE unique_index_data ("
1509
"index_id INTEGER NOT NULL, "
1510
"value NOT NULL, "
1511
"object_data_key NOT NULL, "
1512
"object_data_id INTEGER NOT NULL, "
1513
"PRIMARY KEY (index_id, value, object_data_key), "
1514
"UNIQUE (index_id, value), "
1515
"FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1516
"CASCADE "
1517
"FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
1518
"CASCADE"
1519
");"));
1520
if (NS_WARN_IF(NS_FAILED(rv))) {
1521
return rv;
1522
}
1523
1524
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1525
"INSERT INTO unique_index_data "
1526
"SELECT index_id, value, object_data_key, object_data_id "
1527
"FROM temp_upgrade;"));
1528
if (NS_WARN_IF(NS_FAILED(rv))) {
1529
return rv;
1530
}
1531
1532
rv = aConnection->ExecuteSimpleSQL(
1533
NS_LITERAL_CSTRING("DROP TABLE temp_upgrade;"));
1534
if (NS_WARN_IF(NS_FAILED(rv))) {
1535
return rv;
1536
}
1537
1538
rv = aConnection->ExecuteSimpleSQL(
1539
NS_LITERAL_CSTRING("CREATE INDEX unique_index_data_object_data_id_index "
1540
"ON unique_index_data (object_data_id);"));
1541
if (NS_WARN_IF(NS_FAILED(rv))) {
1542
return rv;
1543
}
1544
1545
// Fix up the ai_index_data table. We're reordering the columns as well as
1546
// changing the primary key from being a simple id to being a composite.
1547
rv = aConnection->ExecuteSimpleSQL(
1548
NS_LITERAL_CSTRING("CREATE TEMPORARY TABLE temp_upgrade ("
1549
"index_id, "
1550
"value, "
1551
"ai_object_data_id "
1552
");"));
1553
if (NS_WARN_IF(NS_FAILED(rv))) {
1554
return rv;
1555
}
1556
1557
rv = aConnection->ExecuteSimpleSQL(
1558
NS_LITERAL_CSTRING("INSERT INTO temp_upgrade "
1559
"SELECT index_id, value, ai_object_data_id "
1560
"FROM ai_index_data;"));
1561
if (NS_WARN_IF(NS_FAILED(rv))) {
1562
return rv;
1563
}
1564
1565
rv = aConnection->ExecuteSimpleSQL(
1566
NS_LITERAL_CSTRING("DROP TABLE ai_index_data;"));
1567
if (NS_WARN_IF(NS_FAILED(rv))) {
1568
return rv;
1569
}
1570
1571
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1572
"CREATE TABLE ai_index_data ("
1573
"index_id INTEGER NOT NULL, "
1574
"value NOT NULL, "
1575
"ai_object_data_id INTEGER NOT NULL, "
1576
"PRIMARY KEY (index_id, value, ai_object_data_id), "
1577
"FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1578
"CASCADE, "
1579
"FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
1580
"CASCADE"
1581
");"));
1582
if (NS_WARN_IF(NS_FAILED(rv))) {
1583
return rv;
1584
}
1585
1586
rv = aConnection->ExecuteSimpleSQL(
1587
NS_LITERAL_CSTRING("INSERT OR IGNORE INTO ai_index_data "
1588
"SELECT index_id, value, ai_object_data_id "
1589
"FROM temp_upgrade;"));
1590
if (NS_WARN_IF(NS_FAILED(rv))) {
1591
return rv;
1592
}
1593
1594
rv = aConnection->ExecuteSimpleSQL(
1595
NS_LITERAL_CSTRING("DROP TABLE temp_upgrade;"));
1596
if (NS_WARN_IF(NS_FAILED(rv))) {
1597
return rv;
1598
}
1599
1600
rv = aConnection->ExecuteSimpleSQL(
1601
NS_LITERAL_CSTRING("CREATE INDEX ai_index_data_ai_object_data_id_index "
1602
"ON ai_index_data (ai_object_data_id);"));
1603
if (NS_WARN_IF(NS_FAILED(rv))) {
1604
return rv;
1605
}
1606
1607
// Fix up the ai_unique_index_data table. We're reordering the columns as well
1608
// as changing the primary key from being a simple id to being a composite.
1609
rv = aConnection->ExecuteSimpleSQL(
1610
NS_LITERAL_CSTRING("CREATE TEMPORARY TABLE temp_upgrade ("
1611
"index_id, "
1612
"value, "
1613
"ai_object_data_id "
1614
");"));
1615
if (NS_WARN_IF(NS_FAILED(rv))) {
1616
return rv;
1617
}
1618
1619
rv = aConnection->ExecuteSimpleSQL(
1620
NS_LITERAL_CSTRING("INSERT INTO temp_upgrade "
1621
"SELECT index_id, value, ai_object_data_id "
1622
"FROM ai_unique_index_data;"));
1623
if (NS_WARN_IF(NS_FAILED(rv))) {
1624
return rv;
1625
}
1626
1627
rv = aConnection->ExecuteSimpleSQL(
1628
NS_LITERAL_CSTRING("DROP TABLE ai_unique_index_data;"));
1629
if (NS_WARN_IF(NS_FAILED(rv))) {
1630
return rv;
1631
}
1632
1633
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1634
"CREATE TABLE ai_unique_index_data ("
1635
"index_id INTEGER NOT NULL, "
1636
"value NOT NULL, "
1637
"ai_object_data_id INTEGER NOT NULL, "
1638
"UNIQUE (index_id, value), "
1639
"PRIMARY KEY (index_id, value, ai_object_data_id), "
1640
"FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
1641
"CASCADE, "
1642
"FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
1643
"CASCADE"
1644
");"));
1645
if (NS_WARN_IF(NS_FAILED(rv))) {
1646
return rv;
1647
}
1648
1649
rv = aConnection->ExecuteSimpleSQL(
1650
NS_LITERAL_CSTRING("INSERT INTO ai_unique_index_data "
1651
"SELECT index_id, value, ai_object_data_id "
1652
"FROM temp_upgrade;"));
1653
if (NS_WARN_IF(NS_FAILED(rv))) {
1654
return rv;
1655
}
1656
1657
rv = aConnection->ExecuteSimpleSQL(
1658
NS_LITERAL_CSTRING("DROP TABLE temp_upgrade;"));
1659
if (NS_WARN_IF(NS_FAILED(rv))) {
1660
return rv;
1661
}
1662
1663
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1664
"CREATE INDEX ai_unique_index_data_ai_object_data_id_index "
1665
"ON ai_unique_index_data (ai_object_data_id);"));
1666
if (NS_WARN_IF(NS_FAILED(rv))) {
1667
return rv;
1668
}
1669
1670
rv = aConnection->SetSchemaVersion(6);
1671
if (NS_WARN_IF(NS_FAILED(rv))) {
1672
return rv;
1673
}
1674
1675
return NS_OK;
1676
}
1677
1678
nsresult UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection) {
1679
AssertIsOnIOThread();
1680
MOZ_ASSERT(aConnection);
1681
1682
AUTO_PROFILER_LABEL("UpgradeSchemaFrom6To7", DOM);
1683
1684
nsresult rv = aConnection->ExecuteSimpleSQL(
1685
NS_LITERAL_CSTRING("CREATE TEMPORARY TABLE temp_upgrade ("
1686
"id, "
1687
"name, "
1688
"key_path, "
1689
"auto_increment"
1690
");"));
1691
if (NS_WARN_IF(NS_FAILED(rv))) {
1692
return rv;
1693
}
1694
1695
rv = aConnection->ExecuteSimpleSQL(
1696
NS_LITERAL_CSTRING("INSERT INTO temp_upgrade "
1697
"SELECT id, name, key_path, auto_increment "
1698
"FROM object_store;"));
1699
if (NS_WARN_IF(NS_FAILED(rv))) {
1700
return rv;
1701
}
1702
1703
rv = aConnection->ExecuteSimpleSQL(
1704
NS_LITERAL_CSTRING("DROP TABLE object_store;"));
1705
if (NS_WARN_IF(NS_FAILED(rv))) {
1706
return rv;
1707
}
1708
1709
rv = aConnection->ExecuteSimpleSQL(
1710
NS_LITERAL_CSTRING("CREATE TABLE object_store ("
1711
"id INTEGER PRIMARY KEY, "
1712
"auto_increment INTEGER NOT NULL DEFAULT 0, "
1713
"name TEXT NOT NULL, "
1714
"key_path TEXT, "
1715
"UNIQUE (name)"
1716
");"));
1717
if (NS_WARN_IF(NS_FAILED(rv))) {
1718
return rv;
1719
}
1720
1721
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1722
"INSERT INTO object_store "
1723
"SELECT id, auto_increment, name, nullif(key_path, '') "
1724
"FROM temp_upgrade;"));
1725
if (NS_WARN_IF(NS_FAILED(rv))) {
1726
return rv;
1727
}
1728
1729
rv = aConnection->ExecuteSimpleSQL(
1730
NS_LITERAL_CSTRING("DROP TABLE temp_upgrade;"));
1731
if (NS_WARN_IF(NS_FAILED(rv))) {
1732
return rv;
1733
}
1734
1735
rv = aConnection->SetSchemaVersion(7);
1736
if (NS_WARN_IF(NS_FAILED(rv))) {
1737
return rv;
1738
}
1739
1740
return NS_OK;
1741
}
1742
1743
nsresult UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection) {
1744
AssertIsOnIOThread();
1745
MOZ_ASSERT(aConnection);
1746
1747
AUTO_PROFILER_LABEL("UpgradeSchemaFrom7To8", DOM);
1748
1749
nsresult rv = aConnection->ExecuteSimpleSQL(
1750
NS_LITERAL_CSTRING("CREATE TEMPORARY TABLE temp_upgrade ("
1751
"id, "
1752
"object_store_id, "
1753
"name, "
1754
"key_path, "
1755
"unique_index, "
1756
"object_store_autoincrement"
1757
");"));
1758
if (NS_WARN_IF(NS_FAILED(rv))) {
1759
return rv;
1760
}
1761
1762
rv = aConnection->ExecuteSimpleSQL(
1763
NS_LITERAL_CSTRING("INSERT INTO temp_upgrade "
1764
"SELECT id, object_store_id, name, key_path, "
1765
"unique_index, object_store_autoincrement "
1766
"FROM object_store_index;"));
1767
if (NS_WARN_IF(NS_FAILED(rv))) {
1768
return rv;
1769
}
1770
1771
rv = aConnection->ExecuteSimpleSQL(
1772
NS_LITERAL_CSTRING("DROP TABLE object_store_index;"));
1773
if (NS_WARN_IF(NS_FAILED(rv))) {
1774
return rv;
1775
}
1776
1777
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
1778
"CREATE TABLE object_store_index ("
1779
"id INTEGER, "
1780
"object_store_id INTEGER NOT NULL, "
1781
"name TEXT NOT NULL, "
1782
"key_path TEXT NOT NULL, "
1783
"unique_index INTEGER NOT NULL, "
1784
"multientry INTEGER NOT NULL, "
1785
"object_store_autoincrement INTERGER NOT NULL, "
1786
"PRIMARY KEY (id), "
1787
"UNIQUE (object_store_id, name), "
1788
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
1789
"CASCADE"
1790
");"));
1791
if (NS_WARN_IF(NS_FAILED(rv))) {
1792
return rv;
1793
}
1794
1795
rv = aConnection->ExecuteSimpleSQL(
1796
NS_LITERAL_CSTRING("INSERT INTO object_store_index "
1797
"SELECT id, object_store_id, name, key_path, "
1798
"unique_index, 0, object_store_autoincrement "
1799
"FROM temp_upgrade;"));
1800
if (NS_WARN_IF(NS_FAILED(rv))) {
1801
return rv;
1802
}
1803
1804
rv = aConnection->ExecuteSimpleSQL(
1805
NS_LITERAL_CSTRING("DROP TABLE temp_upgrade;"));
1806
if (NS_WARN_IF(NS_FAILED(rv))) {
1807
return rv;
1808
}
1809
1810
rv = aConnection->SetSchemaVersion(8);
1811
if (NS_WARN_IF(NS_FAILED(rv))) {
1812
return rv;
1813
}
1814
1815
return NS_OK;
1816
}
1817
1818
class CompressDataBlobsFunction final : public mozIStorageFunction {
1819
public:
1820
NS_DECL_ISUPPORTS
1821
1822
private:
1823
~CompressDataBlobsFunction() = default;
1824
1825
NS_IMETHOD
1826
OnFunctionCall(mozIStorageValueArray* aArguments,
1827
nsIVariant** aResult) override {
1828
MOZ_ASSERT(aArguments);
1829
MOZ_ASSERT(aResult);
1830
1831
AUTO_PROFILER_LABEL("CompressDataBlobsFunction::OnFunctionCall", DOM);
1832
1833
uint32_t argc;
1834
nsresult rv = aArguments->GetNumEntries(&argc);
1835
if (NS_WARN_IF(NS_FAILED(rv))) {
1836
return rv;
1837
}
1838
1839
if (argc != 1) {
1840
NS_WARNING("Don't call me with the wrong number of arguments!");
1841
return NS_ERROR_UNEXPECTED;
1842
}
1843
1844
int32_t type;
1845
rv = aArguments->GetTypeOfIndex(0, &type);
1846
if (NS_WARN_IF(NS_FAILED(rv))) {
1847
return rv;
1848
}
1849
1850
if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
1851
NS_WARNING("Don't call me with the wrong type of arguments!");
1852
return NS_ERROR_UNEXPECTED;
1853
}
1854
1855
const uint8_t* uncompressed;
1856
uint32_t uncompressedLength;
1857
rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed);
1858
if (NS_WARN_IF(NS_FAILED(rv))) {
1859
return rv;
1860
}
1861
1862
size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
1863
UniqueFreePtr<uint8_t> compressed(
1864
static_cast<uint8_t*>(malloc(compressedLength)));
1865
if (NS_WARN_IF(!compressed)) {
1866
return NS_ERROR_OUT_OF_MEMORY;
1867
}
1868
1869
snappy::RawCompress(
1870
reinterpret_cast<const char*>(uncompressed), uncompressedLength,
1871
reinterpret_cast<char*>(compressed.get()), &compressedLength);
1872
1873
std::pair<uint8_t*, int> data(compressed.release(), int(compressedLength));
1874
1875
nsCOMPtr<nsIVariant> result =
1876
new mozilla::storage::AdoptedBlobVariant(data);
1877
1878
result.forget(aResult);
1879
return NS_OK;
1880
}
1881
};
1882
1883
nsresult UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection) {
1884
AssertIsOnIOThread();
1885
MOZ_ASSERT(aConnection);
1886
1887
AUTO_PROFILER_LABEL("UpgradeSchemaFrom8To9_0", DOM);
1888
1889
// We no longer use the dataVersion column.
1890
nsresult rv = aConnection->ExecuteSimpleSQL(
1891
NS_LITERAL_CSTRING("UPDATE database SET dataVersion = 0;"));
1892
if (NS_WARN_IF(NS_FAILED(rv))) {
1893
return rv;
1894
}
1895
1896
nsCOMPtr<mozIStorageFunction> compressor = new CompressDataBlobsFunction();
1897
1898
NS_NAMED_LITERAL_CSTRING(compressorName, "compress");
1899
1900
rv = aConnection->CreateFunction(compressorName, 1, compressor);
1901
if (NS_WARN_IF(NS_FAILED(rv))) {
1902
return rv;
1903
}
1904
1905
// Turn off foreign key constraints before we do anything here.
1906
rv = aConnection->ExecuteSimpleSQL(
1907
NS_LITERAL_CSTRING("UPDATE object_data SET data = compress(data);"));
1908
if (NS_WARN_IF(NS_FAILED(rv))) {
1909
return rv;
1910
}
1911
1912
rv = aConnection->ExecuteSimpleSQL(
1913
NS_LITERAL_CSTRING("UPDATE ai_object_data SET data = compress(data);"));
1914
if (NS_WARN_IF(NS_FAILED(rv))) {
1915
return rv;
1916
}
1917
1918
rv = aConnection->RemoveFunction(compressorName);
1919
if (NS_WARN_IF(NS_FAILED(rv))) {
1920
return rv;
1921
}
1922
1923
rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0));
1924
if (NS_WARN_IF(NS_FAILED(rv))) {
1925
return rv;
1926
}
1927
1928
return NS_OK;
1929
}
1930
1931
nsresult UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection) {
1932
AssertIsOnIOThread();
1933
MOZ_ASSERT(aConnection);
1934
1935
AUTO_PROFILER_LABEL("UpgradeSchemaFrom9_0To10_0", DOM);
1936
1937
nsresult rv = aConnection->ExecuteSimpleSQL(
1938
NS_LITERAL_CSTRING("ALTER TABLE object_data ADD COLUMN file_ids TEXT;"));
1939
if (NS_WARN_IF(NS_FAILED(rv))) {
1940
return rv;
1941
}