diff --git a/app/server/lib/ActiveDoc.ts b/app/server/lib/ActiveDoc.ts index 07859c8e..95e64c70 100644 --- a/app/server/lib/ActiveDoc.ts +++ b/app/server/lib/ActiveDoc.ts @@ -87,7 +87,7 @@ import { makeExceptionalDocSession, OptDocSession } from './DocSession'; -import {DocStorage} from './DocStorage'; +import {createAttachmentsIndex, DocStorage} from './DocStorage'; import {expandQuery} from './ExpandedQuery'; import {GranularAccess, GranularAccessForBundle} from './GranularAccess'; import {OnDemandActions} from './OnDemandActions'; @@ -449,6 +449,9 @@ export class ActiveDoc extends EventEmitter { const initBundle = await this._rawPyCall('apply_user_actions', [["InitNewDoc", timezone, locale]]); await this.docStorage.execTransaction(() => this.docStorage.applyStoredActions(getEnvContent(initBundle.stored))); + // DocStorage can't create this index in the initial schema + // because the table _grist_Attachments doesn't exist at that point - it's created by InitNewDoc. + await createAttachmentsIndex(this.docStorage); await this._initDoc(docSession); await this._tableMetadataLoader.clean(); diff --git a/app/server/lib/DocStorage.ts b/app/server/lib/DocStorage.ts index c30890af..2b8793a6 100644 --- a/app/server/lib/DocStorage.ts +++ b/app/server/lib/DocStorage.ts @@ -381,11 +381,11 @@ export class DocStorage implements ISQLiteDB, OnDemandStorage { // Migration to add an index to _grist_Attachments.fileIdent for fast joining against _gristsys_Files.ident. const tables = await db.all(`SELECT * FROM sqlite_master WHERE type='table' AND name='_grist_Attachments'`); if (!tables.length) { - // _grist_Attachments is created in the first Python migration. - // For the sake of migration tests on ancient documents, just skip this migration if the table doesn't exist. + // _grist_Attachments is created in the first Python migration so doesn't exist here for new documents. + // createAttachmentsIndex is called separately by ActiveDoc for that. return; } - await db.exec(`CREATE INDEX _grist_Attachments_fileIdent ON _grist_Attachments(fileIdent)`); + await createAttachmentsIndex(db); }, ] @@ -661,7 +661,8 @@ export class DocStorage implements ISQLiteDB, OnDemandStorage { /** * Creates a new SQLite database. Will throw an error if the database already exists. - * After a database is created it should be initialized by applying the InitNewDoc action. + * After a database is created it should be initialized by applying the InitNewDoc action + * or by executing the initialDocSql. */ public createFile(): Promise { // It turns out to be important to return a bluebird promise, a lot of code outside @@ -1698,3 +1699,10 @@ export interface IndexColumns { export interface IndexInfo extends IndexColumns { indexId: string; // name of index } + +/** + * Creates an index that allows fast SQL JOIN between _grist_Attachments.fileIdent and _gristsys_Files.ident. + */ +export async function createAttachmentsIndex(db: ISQLiteDB) { + await db.exec(`CREATE INDEX _grist_Attachments_fileIdent ON _grist_Attachments(fileIdent)`); +} diff --git a/app/server/lib/SQLiteDB.ts b/app/server/lib/SQLiteDB.ts index d73db0e5..a6c29f6e 100644 --- a/app/server/lib/SQLiteDB.ts +++ b/app/server/lib/SQLiteDB.ts @@ -148,7 +148,7 @@ export interface ISQLiteDB { * SQLiteDB.openDB(): Opens a DB, and initialize or migrate it to correct schema. * db.execTransaction(cb): Runs a callback in the context of a new DB transaction. */ -export class SQLiteDB { +export class SQLiteDB implements ISQLiteDB { /** * Opens a database or creates a new one, according to OpenMode enum. The schemaInfo specifies * how to initialize a new database, and how to migrate an existing one from an older version. diff --git a/app/server/lib/initialDocSql.ts b/app/server/lib/initialDocSql.ts index 89aa02f5..8f0e9d1d 100644 --- a/app/server/lib/initialDocSql.ts +++ b/app/server/lib/initialDocSql.ts @@ -34,6 +34,7 @@ INSERT INTO _grist_ACLPrincipals VALUES(3,'group','','','Editors',''); INSERT INTO _grist_ACLPrincipals VALUES(4,'group','','','Viewers',''); CREATE TABLE IF NOT EXISTS "_grist_ACLMemberships" (id INTEGER PRIMARY KEY, "parent" INTEGER DEFAULT 0, "child" INTEGER DEFAULT 0); CREATE TABLE IF NOT EXISTS "_grist_Filters" (id INTEGER PRIMARY KEY, "viewSectionRef" INTEGER DEFAULT 0, "colRef" INTEGER DEFAULT 0, "filter" TEXT DEFAULT ''); +CREATE INDEX _grist_Attachments_fileIdent ON _grist_Attachments(fileIdent); COMMIT; `; @@ -86,5 +87,6 @@ INSERT INTO _grist_ACLPrincipals VALUES(4,'group','','','Viewers',''); CREATE TABLE IF NOT EXISTS "_grist_ACLMemberships" (id INTEGER PRIMARY KEY, "parent" INTEGER DEFAULT 0, "child" INTEGER DEFAULT 0); CREATE TABLE IF NOT EXISTS "_grist_Filters" (id INTEGER PRIMARY KEY, "viewSectionRef" INTEGER DEFAULT 0, "colRef" INTEGER DEFAULT 0, "filter" TEXT DEFAULT ''); CREATE TABLE IF NOT EXISTS "Table1" (id INTEGER PRIMARY KEY, "manualSort" NUMERIC DEFAULT 1e999, "A" BLOB DEFAULT NULL, "B" BLOB DEFAULT NULL, "C" BLOB DEFAULT NULL); +CREATE INDEX _grist_Attachments_fileIdent ON _grist_Attachments(fileIdent); COMMIT; `;