diff --git a/chrome/content/zotero/xpcom/db.js b/chrome/content/zotero/xpcom/db.js index 4553876936..31078fb4e2 100644 --- a/chrome/content/zotero/xpcom/db.js +++ b/chrome/content/zotero/xpcom/db.js @@ -105,7 +105,8 @@ Zotero.DBConnection = function (dbNameOrPath) { }; this._dbIsCorrupt = null this._onlineBackupInProgress = false; - + this._onConnectCallbacks = []; + this._transactionPromise = null; if (dbNameOrPath == 'zotero') { @@ -987,6 +988,21 @@ Zotero.DBConnection.prototype.closeDatabase = async function (permanent) { }; +/** + * Register a callback to run after the SQLite connection is opened, including after it has + * been closed and reopened (e.g., after vacuum()). Use this for per-connection state that + * doesn't persist across reopens, such as ATTACHed databases or temp tables. + * + * The callback is not invoked for the connection that's already open at registration time -- + * the caller is responsible for any initial setup. + * + * @param {Function} callback - Async function called with no arguments after each (re)open + */ +Zotero.DBConnection.prototype.onConnect = function (callback) { + this._onConnectCallbacks.push(callback); +}; + + /** * @deprecated */ @@ -1302,7 +1318,16 @@ Zotero.DBConnection.prototype._getConnectionAsync = async function () { idleService.addIdleObserver(this, 300); }); } - + + for (let callback of this._onConnectCallbacks) { + try { + await callback(); + } + catch (e) { + Zotero.logError(e); + } + } + return this._connection; }; diff --git a/chrome/content/zotero/xpcom/fulltext.js b/chrome/content/zotero/xpcom/fulltext.js index 52ee6428a7..4fcefe2b61 100644 --- a/chrome/content/zotero/xpcom/fulltext.js +++ b/chrome/content/zotero/xpcom/fulltext.js @@ -61,9 +61,15 @@ Zotero.Fulltext = Zotero.FullText = new function () { var _syncLibraryVersion = 0; this.init = async function () { - await Zotero.DB.queryAsync("ATTACH ':memory:' AS 'indexing'"); - await Zotero.DB.queryAsync('CREATE TABLE indexing.fulltextWords (word NOT NULL)'); - + let setUpIndexingDB = async () => { + await Zotero.DB.queryAsync("ATTACH ':memory:' AS 'indexing'"); + await Zotero.DB.queryAsync('CREATE TABLE indexing.fulltextWords (word NOT NULL)'); + }; + await setUpIndexingDB(); + // ATTACHed databases don't survive a connection reopen (e.g., after vacuum), so + // re-run the setup on every reconnect + Zotero.DB.onConnect(setUpIndexingDB); + let pdfConverterFileName = "pdftotext"; let pdfInfoFileName = "pdfinfo"; diff --git a/test/tests/dbTest.js b/test/tests/dbTest.js index 63bd7afcf0..3876d17ebd 100644 --- a/test/tests/dbTest.js +++ b/test/tests/dbTest.js @@ -453,4 +453,16 @@ describe("Zotero.DB", function () { assert.isTrue(await IOUtils.exists(bakFile)); }); }); + + describe("#onConnect()", function () { + it("should run registered callbacks after the connection is reopened", async function () { + let count = 0; + Zotero.DB.onConnect(async () => { + count++; + }); + await Zotero.DB.closeDatabase(); + await Zotero.DB.valueQueryAsync("SELECT 1"); + assert.equal(count, 1); + }); + }); }); diff --git a/test/tests/fulltextTest.js b/test/tests/fulltextTest.js index 8a0a565277..29e2b9b435 100644 --- a/test/tests/fulltextTest.js +++ b/test/tests/fulltextTest.js @@ -62,6 +62,16 @@ describe("Zotero.FullText", function () { ); }); + it("should still work after the DB connection is reopened", async function () { + var item = await importFileAttachment('test.txt'); + await Zotero.DB.vacuum({ force: true }); + await Zotero.Fulltext.indexItems([item.id]); + assert.equal( + ((await Zotero.Fulltext.getIndexedState(item))), + Zotero.Fulltext.INDEX_STATE_INDEXED + ); + }); + describe("Indexing with HiddenBrowser", () => { it("should index attachment as its attachmentContentType when supported", async function () { // Firefox would normally load this as text/x-shellscript, but we detect text/plain