| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010 Google Inc. All rights reserved. | 2 * Copyright (C) 2010 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * | 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
| (...skipping 17 matching lines...) Expand all Loading... |
| 28 | 28 |
| 29 #include "config.h" | 29 #include "config.h" |
| 30 #include "IDBFactoryBackendImpl.h" | 30 #include "IDBFactoryBackendImpl.h" |
| 31 | 31 |
| 32 #include "DOMStringList.h" | 32 #include "DOMStringList.h" |
| 33 #include "FileSystem.h" | 33 #include "FileSystem.h" |
| 34 #include "IDBDatabaseBackendImpl.h" | 34 #include "IDBDatabaseBackendImpl.h" |
| 35 #include "IDBDatabaseException.h" | 35 #include "IDBDatabaseException.h" |
| 36 #include "IDBSQLiteDatabase.h" | 36 #include "IDBSQLiteDatabase.h" |
| 37 #include "IDBTransactionCoordinator.h" | 37 #include "IDBTransactionCoordinator.h" |
| 38 #include "SQLiteStatement.h" |
| 39 #include "SQLiteTransaction.h" |
| 38 #include "SecurityOrigin.h" | 40 #include "SecurityOrigin.h" |
| 39 #include <wtf/Threading.h> | 41 #include <wtf/Threading.h> |
| 40 #include <wtf/UnusedParam.h> | 42 #include <wtf/UnusedParam.h> |
| 41 | 43 |
| 42 #if ENABLE(INDEXED_DATABASE) | 44 #if ENABLE(INDEXED_DATABASE) |
| 43 | 45 |
| 44 namespace WebCore { | 46 namespace WebCore { |
| 45 | 47 |
| 46 IDBFactoryBackendImpl::IDBFactoryBackendImpl() | 48 IDBFactoryBackendImpl::IDBFactoryBackendImpl() |
| 47 : m_transactionCoordinator(IDBTransactionCoordinator::create()) | 49 : m_transactionCoordinator(IDBTransactionCoordinator::create()) |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 86 | 88 |
| 87 // FIXME: Error checking? | 89 // FIXME: Error checking? |
| 88 sqliteDatabase->db().setMaximumSize(maximumSize); | 90 sqliteDatabase->db().setMaximumSize(maximumSize); |
| 89 sqliteDatabase->db().turnOnIncrementalAutoVacuum(); | 91 sqliteDatabase->db().turnOnIncrementalAutoVacuum(); |
| 90 | 92 |
| 91 return sqliteDatabase.release(); | 93 return sqliteDatabase.release(); |
| 92 } | 94 } |
| 93 | 95 |
| 94 static bool createTables(SQLiteDatabase& sqliteDatabase) | 96 static bool createTables(SQLiteDatabase& sqliteDatabase) |
| 95 { | 97 { |
| 98 if (sqliteDatabase.tableExists("Databases")) |
| 99 return true; |
| 100 |
| 96 static const char* commands[] = { | 101 static const char* commands[] = { |
| 97 "CREATE TABLE IF NOT EXISTS Databases (id INTEGER PRIMARY KEY, name TEXT
NOT NULL, description TEXT NOT NULL, version TEXT NOT NULL)", | 102 "CREATE TABLE Databases (id INTEGER PRIMARY KEY, name TEXT NOT NULL, des
cription TEXT NOT NULL, version TEXT NOT NULL)", |
| 98 "CREATE UNIQUE INDEX IF NOT EXISTS Databases_name ON Databases(name)", | 103 "CREATE UNIQUE INDEX Databases_name ON Databases(name)", |
| 99 | 104 |
| 100 "CREATE TABLE IF NOT EXISTS ObjectStores (id INTEGER PRIMARY KEY, name T
EXT NOT NULL, keyPath TEXT, doAutoIncrement INTEGER NOT NULL, databaseId INTEGER
NOT NULL REFERENCES Databases(id))", | 105 "CREATE TABLE ObjectStores (id INTEGER PRIMARY KEY, name TEXT NOT NULL,
keyPath TEXT, doAutoIncrement INTEGER NOT NULL, databaseId INTEGER NOT NULL REFE
RENCES Databases(id))", |
| 101 "CREATE UNIQUE INDEX IF NOT EXISTS ObjectStores_composit ON ObjectStores
(databaseId, name)", | 106 "CREATE UNIQUE INDEX ObjectStores_composit ON ObjectStores(databaseId, n
ame)", |
| 102 | 107 |
| 103 "CREATE TABLE IF NOT EXISTS Indexes (id INTEGER PRIMARY KEY, objectStore
Id INTEGER NOT NULL REFERENCES ObjectStore(id), name TEXT NOT NULL, keyPath TEXT
, isUnique INTEGER NOT NULL)", | 108 "CREATE TABLE Indexes (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT
NULL REFERENCES ObjectStore(id), name TEXT NOT NULL, keyPath TEXT, isUnique INT
EGER NOT NULL)", |
| 104 "CREATE UNIQUE INDEX IF NOT EXISTS Indexes_composit ON Indexes(objectSto
reId, name)", | 109 "CREATE UNIQUE INDEX Indexes_composit ON Indexes(objectStoreId, name)", |
| 105 | 110 |
| 106 "CREATE TABLE IF NOT EXISTS ObjectStoreData (id INTEGER PRIMARY KEY, obj
ectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate
INTEGER, keyNumber INTEGER, value TEXT NOT NULL)", | 111 "CREATE TABLE ObjectStoreData (id INTEGER PRIMARY KEY, objectStoreId INT
EGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate INTEGER, keyNu
mber INTEGER, value TEXT NOT NULL)", |
| 107 "CREATE UNIQUE INDEX IF NOT EXISTS ObjectStoreData_composit ON ObjectSto
reData(keyString, keyDate, keyNumber, objectStoreId)", | 112 "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyStri
ng, keyDate, keyNumber, objectStoreId)", |
| 108 | 113 |
| 109 "CREATE TABLE IF NOT EXISTS IndexData (id INTEGER PRIMARY KEY, indexId I
NTEGER NOT NULL REFERENCES Indexes(id), keyString TEXT, keyDate INTEGER, keyNumb
er INTEGER, objectStoreDataId INTEGER NOT NULL REFERENCES ObjectStoreData(id))", | 114 "CREATE TABLE IndexData (id INTEGER PRIMARY KEY, indexId INTEGER NOT NUL
L REFERENCES Indexes(id), keyString TEXT, keyDate INTEGER, keyNumber INTEGER, ob
jectStoreDataId INTEGER NOT NULL REFERENCES ObjectStoreData(id))", |
| 110 "CREATE INDEX IF NOT EXISTS IndexData_composit ON IndexData(keyString, k
eyDate, keyNumber, indexId)", | 115 "CREATE INDEX IndexData_composit ON IndexData(keyString, keyDate, keyNum
ber, indexId)", |
| 111 "CREATE INDEX IF NOT EXISTS IndexData_objectStoreDataId ON IndexData(obj
ectStoreDataId)", | 116 "CREATE INDEX IndexData_objectStoreDataId ON IndexData(objectStoreDataId
)", |
| 112 "CREATE INDEX IF NOT EXISTS IndexData_indexId ON IndexData(indexId)" | 117 "CREATE INDEX IndexData_indexId ON IndexData(indexId)", |
| 113 }; | 118 }; |
| 114 | 119 |
| 120 SQLiteTransaction transaction(sqliteDatabase, false); |
| 121 transaction.begin(); |
| 115 for (size_t i = 0; i < arraysize(commands); ++i) { | 122 for (size_t i = 0; i < arraysize(commands); ++i) { |
| 116 if (!sqliteDatabase.executeCommand(commands[i])) { | 123 if (!sqliteDatabase.executeCommand(commands[i])) { |
| 117 // FIXME: We should try to recover from this situation. Maybe nuke t
he database and start over? | 124 // FIXME: We should try to recover from this situation. Maybe nuke t
he database and start over? |
| 118 LOG_ERROR("Failed to run the following command for IndexedDB: %s", c
ommands[i]); | 125 LOG_ERROR("Failed to run the following command for IndexedDB: %s", c
ommands[i]); |
| 119 return false; | 126 return false; |
| 120 } | 127 } |
| 121 } | 128 } |
| 129 transaction.commit(); |
| 130 return true; |
| 131 } |
| 132 |
| 133 static bool createMetaDataTable(SQLiteDatabase& sqliteDatabase) |
| 134 { |
| 135 static const char* commands[] = { |
| 136 "CREATE TABLE MetaData (name TEXT PRIMARY KEY, value NONE)", |
| 137 "INSERT INTO MetaData VALUES ('version', 1)", |
| 138 }; |
| 139 |
| 140 SQLiteTransaction transaction(sqliteDatabase, false); |
| 141 transaction.begin(); |
| 142 for (size_t i = 0; i < arraysize(commands); ++i) { |
| 143 if (!sqliteDatabase.executeCommand(commands[i])) |
| 144 return false; |
| 145 } |
| 146 transaction.commit(); |
| 147 return true; |
| 148 } |
| 149 |
| 150 static bool getDatabaseVersion(SQLiteDatabase& sqliteDatabase, int* databaseVers
ion) |
| 151 { |
| 152 SQLiteStatement query(sqliteDatabase, "SELECT value FROM MetaData WHERE name
= 'version'"); |
| 153 if (query.prepare() != SQLResultOk || query.step() != SQLResultRow) |
| 154 return false; |
| 155 |
| 156 *databaseVersion = query.getColumnInt(0); |
| 157 return query.finalize() == SQLResultOk; |
| 158 } |
| 159 |
| 160 static bool migrateDatabase(SQLiteDatabase& sqliteDatabase) |
| 161 { |
| 162 if (!sqliteDatabase.tableExists("MetaData")) { |
| 163 if (!createMetaDataTable(sqliteDatabase)) |
| 164 return false; |
| 165 } |
| 166 |
| 167 int databaseVersion; |
| 168 if (!getDatabaseVersion(sqliteDatabase, &databaseVersion)) |
| 169 return false; |
| 170 |
| 171 if (databaseVersion == 1) { |
| 172 static const char* commands[] = { |
| 173 "DROP TABLE IF EXISTS ObjectStoreData2", |
| 174 "CREATE TABLE ObjectStoreData2 (id INTEGER PRIMARY KEY, objectStoreI
d INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate REAL, key
Number REAL, value TEXT NOT NULL)", |
| 175 "INSERT INTO ObjectStoreData2 SELECT * FROM ObjectStoreData", |
| 176 "DROP TABLE ObjectStoreData", // This depends on SQLite not enforcin
g referential consistency. |
| 177 "ALTER TABLE ObjectStoreData2 RENAME TO ObjectStoreData", |
| 178 "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(key
String, keyDate, keyNumber, objectStoreId)", |
| 179 "DROP TABLE IF EXISTS IndexData2", // This depends on SQLite not enf
orcing referential consistency. |
| 180 "CREATE TABLE IndexData2 (id INTEGER PRIMARY KEY, indexId INTEGER NO
T NULL REFERENCES Indexes(id), keyString TEXT, keyDate REAL, keyNumber REAL, obj
ectStoreDataId INTEGER NOT NULL REFERENCES ObjectStoreData(id))", |
| 181 "INSERT INTO IndexData2 SELECT * FROM IndexData", |
| 182 "DROP TABLE IndexData", |
| 183 "ALTER TABLE IndexData2 RENAME TO IndexData", |
| 184 "CREATE INDEX IndexData_composit ON IndexData(keyString, keyDate, ke
yNumber, indexId)", |
| 185 "CREATE INDEX IndexData_objectStoreDataId ON IndexData(objectStoreDa
taId)", |
| 186 "CREATE INDEX IndexData_indexId ON IndexData(indexId)", |
| 187 "UPDATE MetaData SET value = 2 WHERE name = 'version'", |
| 188 }; |
| 189 |
| 190 SQLiteTransaction transaction(sqliteDatabase, false); |
| 191 transaction.begin(); |
| 192 for (size_t i = 0; i < arraysize(commands); ++i) { |
| 193 if (!sqliteDatabase.executeCommand(commands[i])) { |
| 194 LOG_ERROR("Failed to run the following command for IndexedDB: %s
", commands[i]); |
| 195 return false; |
| 196 } |
| 197 } |
| 198 transaction.commit(); |
| 199 |
| 200 databaseVersion = 2; |
| 201 } |
| 202 |
| 122 return true; | 203 return true; |
| 123 } | 204 } |
| 124 | 205 |
| 125 void IDBFactoryBackendImpl::open(const String& name, PassRefPtr<IDBCallbacks> ca
llbacks, PassRefPtr<SecurityOrigin> securityOrigin, Frame*, const String& dataDi
r, int64_t maximumSize) | 206 void IDBFactoryBackendImpl::open(const String& name, PassRefPtr<IDBCallbacks> ca
llbacks, PassRefPtr<SecurityOrigin> securityOrigin, Frame*, const String& dataDi
r, int64_t maximumSize) |
| 126 { | 207 { |
| 127 String fileIdentifier = securityOrigin->databaseIdentifier(); | 208 String fileIdentifier = securityOrigin->databaseIdentifier(); |
| 128 String uniqueIdentifier = fileIdentifier + "@" + name; | 209 String uniqueIdentifier = fileIdentifier + "@" + name; |
| 129 IDBDatabaseBackendMap::iterator it = m_databaseBackendMap.find(uniqueIdentif
ier); | 210 IDBDatabaseBackendMap::iterator it = m_databaseBackendMap.find(uniqueIdentif
ier); |
| 130 if (it != m_databaseBackendMap.end()) { | 211 if (it != m_databaseBackendMap.end()) { |
| 131 callbacks->onSuccess(it->second); | 212 callbacks->onSuccess(it->second); |
| 132 return; | 213 return; |
| 133 } | 214 } |
| 134 | 215 |
| 135 // FIXME: Everything from now on should be done on another thread. | 216 // FIXME: Everything from now on should be done on another thread. |
| 136 | 217 |
| 137 RefPtr<IDBSQLiteDatabase> sqliteDatabase; | 218 RefPtr<IDBSQLiteDatabase> sqliteDatabase; |
| 138 SQLiteDatabaseMap::iterator it2 = m_sqliteDatabaseMap.find(fileIdentifier); | 219 SQLiteDatabaseMap::iterator it2 = m_sqliteDatabaseMap.find(fileIdentifier); |
| 139 if (it2 != m_sqliteDatabaseMap.end()) | 220 if (it2 != m_sqliteDatabaseMap.end()) |
| 140 sqliteDatabase = it2->second; | 221 sqliteDatabase = it2->second; |
| 141 else { | 222 else { |
| 142 sqliteDatabase = openSQLiteDatabase(securityOrigin.get(), dataDir, maxim
umSize, fileIdentifier, this); | 223 sqliteDatabase = openSQLiteDatabase(securityOrigin.get(), dataDir, maxim
umSize, fileIdentifier, this); |
| 143 | 224 |
| 144 if (!sqliteDatabase || !createTables(sqliteDatabase->db())) { | 225 if (!sqliteDatabase || !createTables(sqliteDatabase->db()) || !migrateDa
tabase(sqliteDatabase->db())) { |
| 145 callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UN
KNOWN_ERR, "Internal error.")); | 226 callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UN
KNOWN_ERR, "Internal error.")); |
| 227 m_sqliteDatabaseMap.set(fileIdentifier, 0); |
| 146 return; | 228 return; |
| 147 } | 229 } |
| 148 m_sqliteDatabaseMap.set(fileIdentifier, sqliteDatabase.get()); | 230 m_sqliteDatabaseMap.set(fileIdentifier, sqliteDatabase.get()); |
| 149 } | 231 } |
| 150 | 232 |
| 151 RefPtr<IDBDatabaseBackendImpl> databaseBackend = IDBDatabaseBackendImpl::cre
ate(name, sqliteDatabase.get(), m_transactionCoordinator.get(), this, uniqueIden
tifier); | 233 RefPtr<IDBDatabaseBackendImpl> databaseBackend = IDBDatabaseBackendImpl::cre
ate(name, sqliteDatabase.get(), m_transactionCoordinator.get(), this, uniqueIden
tifier); |
| 152 callbacks->onSuccess(databaseBackend.get()); | 234 callbacks->onSuccess(databaseBackend.get()); |
| 153 m_databaseBackendMap.set(uniqueIdentifier, databaseBackend.get()); | 235 m_databaseBackendMap.set(uniqueIdentifier, databaseBackend.get()); |
| 154 } | 236 } |
| 155 | 237 |
| 156 } // namespace WebCore | 238 } // namespace WebCore |
| 157 | 239 |
| 158 #endif // ENABLE(INDEXED_DATABASE) | 240 #endif // ENABLE(INDEXED_DATABASE) |
| 159 | |
| OLD | NEW |