--- a/toolkit/components/satchel/FormHistory.jsm
+++ b/toolkit/components/satchel/FormHistory.jsm
@@ -256,108 +256,16 @@ function makeQueryPredicates(aQueryData,
return "lastUsed <= :" + field;
}
}
return field + " = :" + field;
}).join(delimiter);
}
-/**
- * Storage statement creation and parameter binding
- */
-
-function makeSearchStatement(aSearchData, aSelectTerms) {
- let query = "SELECT " + aSelectTerms.join(", ") + " FROM moz_formhistory";
- let queryTerms = makeQueryPredicates(aSearchData);
- if (queryTerms) {
- query += " WHERE " + queryTerms;
- }
-
- return dbCreateAsyncStatement(query, aSearchData);
-}
-
-function makeAddStatement(aNewData, aNow, aBindingArrays) {
- let query = "INSERT INTO moz_formhistory " +
- "(fieldname, value, timesUsed, firstUsed, lastUsed, guid) " +
- "VALUES (:fieldname, :value, :timesUsed, :firstUsed, :lastUsed, :guid)";
-
- aNewData.timesUsed = aNewData.timesUsed || 1;
- aNewData.firstUsed = aNewData.firstUsed || aNow;
- aNewData.lastUsed = aNewData.lastUsed || aNow;
- return dbCreateAsyncStatement(query, aNewData, aBindingArrays);
-}
-
-function makeBumpStatement(aGuid, aNow, aBindingArrays) {
- let query = "UPDATE moz_formhistory " +
- "SET timesUsed = timesUsed + 1, lastUsed = :lastUsed WHERE guid = :guid";
- let queryParams = {
- lastUsed: aNow,
- guid: aGuid,
- };
-
- return dbCreateAsyncStatement(query, queryParams, aBindingArrays);
-}
-
-function makeRemoveStatement(aSearchData, aBindingArrays) {
- let query = "DELETE FROM moz_formhistory";
- let queryTerms = makeQueryPredicates(aSearchData);
-
- if (queryTerms) {
- log("removeEntries");
- query += " WHERE " + queryTerms;
- } else {
- log("removeAllEntries");
- // Not specifying any fields means we should remove all entries. We
- // won't need to modify the query in this case.
- }
-
- return dbCreateAsyncStatement(query, aSearchData, aBindingArrays);
-}
-
-function makeUpdateStatement(aGuid, aNewData, aBindingArrays) {
- let query = "UPDATE moz_formhistory SET ";
- let queryTerms = makeQueryPredicates(aNewData, ", ");
-
- if (!queryTerms) {
- throw Components.Exception("Update query must define fields to modify.",
- Cr.NS_ERROR_ILLEGAL_VALUE);
- }
-
- query += queryTerms + " WHERE guid = :existing_guid";
- aNewData.existing_guid = aGuid;
-
- return dbCreateAsyncStatement(query, aNewData, aBindingArrays);
-}
-
-function makeMoveToDeletedStatement(aGuid, aNow, aData, aBindingArrays) {
- if (supportsDeletedTable) {
- let query = "INSERT INTO moz_deleted_formhistory (guid, timeDeleted)";
- let queryTerms = makeQueryPredicates(aData);
-
- if (aGuid) {
- query += " VALUES (:guid, :timeDeleted)";
- } else {
- // TODO: Add these items to the deleted items table once we've sorted
- // out the issues from bug 756701
- if (!queryTerms) {
- return undefined;
- }
-
- query += " SELECT guid, :timeDeleted FROM moz_formhistory WHERE " + queryTerms;
- }
-
- aData.timeDeleted = aNow;
-
- return dbCreateAsyncStatement(query, aData, aBindingArrays);
- }
-
- return null;
-}
-
function generateGUID() {
// string like: "{f60d9eac-9421-4abc-8491-8e8322b063d4}"
let uuid = uuidService.generateUUID().toString();
let raw = ""; // A string with the low bytes set to random values
let bytes = 0;
for (let i = 1; bytes < 12; i += 2) {
// Skip dashes
if (uuid[i] == "-") {
@@ -365,267 +273,32 @@ function generateGUID() {
}
let hexVal = parseInt(uuid[i] + uuid[i + 1], 16);
raw += String.fromCharCode(hexVal);
bytes++;
}
return btoa(raw);
}
-/**
- * Database creation and access
- */
-
-var _dbConnection = null;
-XPCOMUtils.defineLazyGetter(this, "dbConnection", function() {
- let dbFile;
-
- try {
- dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile).clone();
- dbFile.append(DB_FILENAME);
- log("Opening database at " + dbFile.path);
-
- _dbConnection = Services.storage.openDatabase(dbFile);
- dbInit();
- } catch (e) {
- if (e.result != Cr.NS_ERROR_FILE_CORRUPTED) {
- throw e;
- }
- dbCleanup(dbFile);
- _dbConnection = Services.storage.openDatabase(dbFile);
- dbInit();
- }
-
- return _dbConnection;
-});
-
-
-var dbStmts = new Map();
-
-/*
- * dbCreateAsyncStatement
- *
- * Creates a statement, wraps it, and then does parameter replacement
- */
-function dbCreateAsyncStatement(aQuery, aParams, aBindingArrays) {
- if (!aQuery) {
- return null;
- }
-
- let stmt = dbStmts.get(aQuery);
- if (!stmt) {
- log("Creating new statement for query: " + aQuery);
- stmt = dbConnection.createAsyncStatement(aQuery);
- dbStmts.set(aQuery, stmt);
- }
-
- if (aBindingArrays) {
- let bindingArray = aBindingArrays.get(stmt);
- if (!bindingArray) {
- // first time using a particular statement in update
- bindingArray = stmt.newBindingParamsArray();
- aBindingArrays.set(stmt, bindingArray);
- }
-
- if (aParams) {
- let bindingParams = bindingArray.newBindingParams();
- for (let field in aParams) {
- bindingParams.bindByName(field, aParams[field]);
- }
- bindingArray.addParams(bindingParams);
- }
- } else if (aParams) {
- for (let field in aParams) {
- stmt.params[field] = aParams[field];
- }
- }
-
- return stmt;
-}
-
-var dbMigrate;
-
-/**
- * Attempts to initialize the database. This creates the file if it doesn't
- * exist, performs any migrations, etc.
- */
-function dbInit() {
- log("Initializing Database");
-
- if (!_dbConnection.tableExists("moz_formhistory")) {
- dbCreate();
- return;
- }
-
- // When FormHistory is released, we will no longer support the various schema versions prior to
- // this release that nsIFormHistory2 once did.
- let version = _dbConnection.schemaVersion;
- if (version < 3) {
- throw Components.Exception("DB version is unsupported.",
- Cr.NS_ERROR_FILE_CORRUPTED);
- } else if (version != DB_SCHEMA_VERSION) {
- dbMigrate(version);
- }
-}
-
var Migrators = {
/*
* Updates the DB schema to v3 (bug 506402).
* Adds deleted form history table.
*/
- dbMigrateToVersion4() {
- if (!_dbConnection.tableExists("moz_deleted_formhistory")) {
- let table = dbSchema.tables.moz_deleted_formhistory;
- let tSQL = Object.keys(table).map(col => [col, table[col]].join(" ")).join(", ");
- _dbConnection.createTable("moz_deleted_formhistory", tSQL);
- }
- },
-
async dbAsyncMigrateToVersion4(conn) {
const TABLE_NAME = "moz_deleted_formhistory";
let tableExists = await conn.tableExists(TABLE_NAME);
if (!tableExists) {
let table = dbSchema.tables[TABLE_NAME];
let tSQL = Object.keys(table).map(col => [col, table[col]].join(" ")).join(", ");
await conn.execute(`CREATE TABLE ${TABLE_NAME} (${tSQL})`);
}
},
};
-function dbCreate() {
- log("Creating DB -- tables");
- for (let name in dbSchema.tables) {
- let table = dbSchema.tables[name];
- let tSQL = Object.keys(table).map(col => [col, table[col]].join(" ")).join(", ");
- log("Creating table " + name + " with " + tSQL);
- _dbConnection.createTable(name, tSQL);
- }
-
- log("Creating DB -- indices");
- for (let name in dbSchema.indices) {
- let index = dbSchema.indices[name];
- let statement = "CREATE INDEX IF NOT EXISTS " + name + " ON " + index.table +
- "(" + index.columns.join(", ") + ")";
- _dbConnection.executeSimpleSQL(statement);
- }
-
- _dbConnection.schemaVersion = DB_SCHEMA_VERSION;
-}
-
-dbMigrate = (oldVersion) => {
- log("Attempting to migrate from version " + oldVersion);
-
- if (oldVersion > DB_SCHEMA_VERSION) {
- log("Downgrading to version " + DB_SCHEMA_VERSION);
- // User's DB is newer. Sanity check that our expected columns are
- // present, and if so mark the lower version and merrily continue
- // on. If the columns are borked, something is wrong so blow away
- // the DB and start from scratch. [Future incompatible upgrades
- // should switch to a different table or file.]
-
- if (!dbAreExpectedColumnsPresent()) {
- throw Components.Exception("DB is missing expected columns",
- Cr.NS_ERROR_FILE_CORRUPTED);
- }
-
- // Change the stored version to the current version. If the user
- // runs the newer code again, it will see the lower version number
- // and re-upgrade (to fixup any entries the old code added).
- _dbConnection.schemaVersion = DB_SCHEMA_VERSION;
- return;
- }
-
- // Note that migration is currently performed synchronously.
- _dbConnection.beginTransaction();
-
- try {
- for (let v = oldVersion + 1; v <= DB_SCHEMA_VERSION; v++) {
- this.log("Upgrading to version " + v + "...");
- Migrators["dbMigrateToVersion" + v]();
- }
- } catch (e) {
- this.log("Migration failed: " + e);
- this.dbConnection.rollbackTransaction();
- throw e;
- }
-
- _dbConnection.schemaVersion = DB_SCHEMA_VERSION;
- _dbConnection.commitTransaction();
-
- log("DB migration completed.");
-};
-
-/**
- * Sanity check to ensure that the columns this version of the code expects
- * are present in the DB we're using.
- * @returns {boolean} whether expected columns are present
- */
-function dbAreExpectedColumnsPresent() {
- for (let name in dbSchema.tables) {
- let table = dbSchema.tables[name];
- let query = "SELECT " +
- Object.keys(table).join(", ") +
- " FROM " + name;
- try {
- let stmt = _dbConnection.createStatement(query);
- // (no need to execute statement, if it compiled we're good)
- stmt.finalize();
- } catch (e) {
- return false;
- }
- }
-
- log("verified that expected columns are present in DB.");
- return true;
-}
-
-/**
- * Called when database creation fails. Finalizes database statements,
- * closes the database connection, deletes the database file.
- * @param {Object} dbFile database file to close
- */
-function dbCleanup(dbFile) {
- log("Cleaning up DB file - close & remove & backup");
-
- // Create backup file
- let backupFile = dbFile.leafName + ".corrupt";
- Services.storage.backupDatabaseFile(dbFile, backupFile);
-
- dbClose(false);
- dbFile.remove(false);
-}
-
-function dbClose(aShutdown) {
- log("dbClose(" + aShutdown + ")");
-
- if (aShutdown) {
- sendNotification("formhistory-shutdown", null);
- }
-
- // Connection may never have been created if say open failed but we still
- // end up calling dbClose as part of the rest of dbCleanup.
- if (!_dbConnection) {
- return;
- }
-
- log("dbClose finalize statements");
- for (let stmt of dbStmts.values()) {
- stmt.finalize();
- }
-
- dbStmts = new Map();
-
- let closed = false;
- _dbConnection.asyncClose(() => closed = true);
-
- if (!aShutdown) {
- Services.tm.spinEventLoopUntil(() => closed);
- }
-}
-
/**
* @typedef {Object} InsertQueryData
* @property {Object} updatedChange
* A change requested by FormHistory.
* @property {String} query
* The insert query string.
*/
@@ -664,21 +337,16 @@ function prepareInsertQuery(change, now)
*/
// XXX This should be split up and the complexity reduced.
// eslint-disable-next-line complexity
async function updateFormHistoryWrite(aChanges, aPreparedHandlers) {
log("updateFormHistoryWrite " + aChanges.length);
// pass 'now' down so that every entry in the batch has the same timestamp
let now = Date.now() * 1000;
-
- // for each change, we either create and append a new storage statement to
- // stmts or bind a new set of parameters to an existing storage statement.
- // stmts and bindingArrays are updated when makeXXXStatement eventually
- // calls dbCreateAsyncStatement.
let queries = [];
let notifications = [];
let conn = await FormHistory.db;
for (let change of aChanges) {
let operation = change.op;
delete change.op;
switch (operation) {