diff --git a/toolkit/components/places/PlacesSemanticHistoryDatabase.sys.mjs b/toolkit/components/places/PlacesSemanticHistoryDatabase.sys.mjs index a08f6d2e378f..4aef42bc0c78 100644 --- a/toolkit/components/places/PlacesSemanticHistoryDatabase.sys.mjs +++ b/toolkit/components/places/PlacesSemanticHistoryDatabase.sys.mjs @@ -97,7 +97,7 @@ export class PlacesSemanticHistoryDatabase { lazy.logger.info("Initializing schema"); await this.#initializeSchema(); } catch (e) { - lazy.logger.warn("Schema initialization failed"); + lazy.logger.warn(`Schema initialization failed: ${e}`); // If the schema cannot be initialized close the connection and create // a new database file. await this.closeConnection(); @@ -153,12 +153,15 @@ export class PlacesSemanticHistoryDatabase { */ async #initializeSchema() { let version = await this.#conn.getSchemaVersion(); + lazy.logger.debug(`Database schema version: ${version}`); if (version > CURRENT_SCHEMA_VERSION) { + lazy.logger.warn(`Database schema downgrade`); throw new Error("Downgrade of the schema is not supported"); } if (version == CURRENT_SCHEMA_VERSION) { let healthy = await this.#checkDatabaseEntities(this.#embeddingSize); if (!healthy) { + lazy.logger.error(`Database schema is not healthy`); throw new Error("Database schema is not healthy"); } return; @@ -228,18 +231,21 @@ export class PlacesSemanticHistoryDatabase { !tableNames.includes("vec_history") || !tableNames.includes("vec_history_mapping") ) { + lazy.logger.error(`Missing tables in the database`); return false; } // If the embedding size changed the database should be recreated. This // should be handled by a migration, but we check to be overly safe. - let embeddingSizeCheck = await this.#conn.execute( - `PRAGMA table_info(vec_history)` - ); - let embeddingSizeRow = embeddingSizeCheck.find( - row => row.getResultByName("name") == "embedding" - ); - if (embeddingSizeRow.getResultByName("type") != `FLOAT[${embeddingSize}]`) { + let embeddingSizeMatches = ( + await this.#conn.execute( + `SELECT INSTR(sql, :needle) > 0 + FROM sqlite_master WHERE name = 'vec_history'`, + { needle: `FLOAT[${embeddingSize}]` } + ) + )[0].getResultByIndex(0); + if (!embeddingSizeMatches) { + lazy.logger.error(`Embeddings size doesn't match`); return false; } diff --git a/toolkit/components/places/tests/unit/test_PlacesSemanticHistoryDatabase.js b/toolkit/components/places/tests/unit/test_PlacesSemanticHistoryDatabase.js index 3b6636422472..e1a5e52dc4bd 100644 --- a/toolkit/components/places/tests/unit/test_PlacesSemanticHistoryDatabase.js +++ b/toolkit/components/places/tests/unit/test_PlacesSemanticHistoryDatabase.js @@ -94,3 +94,26 @@ add_task(async function test_corruptdb() { await db.closeConnection(); await db.removeDatabaseFiles(); }); + +add_task(async function test_healthydb() { + let db = new PlacesSemanticHistoryDatabase({ + embeddingSize: 4, + fileName: "places_semantic.sqlite", + }); + await db.getConnection(); + await db.closeConnection(); + // Check database creation time won't change when reopening, as that would + // indicate the database file was replaced. + let creationTime = (await IOUtils.stat(db.databaseFilePath)).creationTime; + db = new PlacesSemanticHistoryDatabase({ + embeddingSize: 4, + fileName: "places_semantic.sqlite", + }); + await db.getConnection(); + await db.closeConnection(); + Assert.equal( + creationTime, + (await IOUtils.stat(db.databaseFilePath)).creationTime, + "Database creation time should not change." + ); +});