Skip to content

inserts with triggers that update related FTS tables croak when wrapped in a transaction #1003

Open
@punkish

Description

@punkish

I have a sequence of inserts wrapped in a transaction somewhat like below

try {
    const treatment {
        journalTitle: 'foo',
        kingdom: 'Animalia',
        phylum: 'Hemiptera',
        …
        treatmentAuthors: [],
        bibRefCitations: [],
        materialCitations: [],
        figureCitations: []
    }

const insertTreatment = (treatment, cache) => {
    // step1: insert journalTitle, kingdom, phylum, etc., and get 
    // their ids, and append them into the treatment object
    
    const { journals_id } = insertJournalGet_journals_id(treatment);
    const { kingdoms_id } = insertKingdomGet_kingdoms_id(treatment);
    …
    treatment.journals_id = journals_id;
    treatment.kingdoms_id = kingdoms_id;
    …

    // step2: insert treatment
    const { treatments_id } = insertTreatmentGet_treatments_id.get(treatment);
    // TRIGGER fires

    // step3: insert bibRefCitations
    const bibRefCitationsCache = {};
    const bibRefCitations = treatment.bibRefCitations;

    if (bibRefCitations.length) {
        for (const bibRefCitation of bibRefCitations) {
            bibRefCitation.treatments_id = treatments_id;
            // INSERT
            const { bibRefCitations_id } = insertBibRefCitationGet_bibRefCitations_id.get(bibRefCitation);
            // TRIGGER fires
            const bibRefCitationId = bibRefCitation.bibRefCitationId;
            bibRefCitations[bibRefCitationId] = bibRefCitations_id;
        }
    }

    … and so on …
}

As noted above with the commend // TRIGGER fires, some of the inserts trigger an insert into related FTS tables. Below is a simplified (but not dumb-ified) schema of the db.

CREATE TABLE bibRefCitations (
    id INTEGER PRIMARY KEY,
    … lots of columns …
    fulltext TEXT COLLATE NOCASE
);

CREATE VIRTUAL TABLE bibRefCitationsFts USING fts5 (
    fulltext,
    content=''
);

CREATE TRIGGER IF NOT EXISTS bc_afterInsert 
    AFTER INSERT ON bibRefCitations 
    BEGIN
        INSERT INTO bibRefCitationsFts(rowid, fulltext) 
        VALUES (new.id, new.fulltext);
    END;`,

CREATE TABLE figureCitations (
    id INTEGER PRIMARY KEY,
    … lots of columns …
    captionText TEXT COLLATE NOCASE
);

CREATE VIRTUAL TABLE figureCitationsFts USING fts5 (
    captionText,
    content=''
);

CREATE TRIGGER IF NOT EXISTS fc_afterInsert 
    AFTER INSERT ON figureCitations 
    BEGIN
        INSERT INTO figureCitationsFts(rowid, captionText) 
        VALUES (new.id, new.captionText);
    END;

CREATE TABLE journals (
    id INTEGER PRIMARY KEY,
    journalTitle TEXT UNIQUE NOT NULL COLLATE NOCASE
);

CREATE TABLE treatments (
    id INTEGER PRIMARY KEY,
    … lots of columns …
    fulltext TEXT COLLATE NOCASE
);

CREATE VIRTUAL TABLE treatmentsFts USING fts5 (
    treatmentTitle,
    fulltext,
    content=''
);

CREATE TRIGGER IF NOT EXISTS tr_afterInsert 
    AFTER INSERT ON treatments 
    BEGIN
        INSERT INTO treatmentsFts(rowid, treatmentTitle, fulltext) 
        VALUES (new.id, new.treatmentTitle, new.fulltext);
    END;

If I disable the triggers, the transaction completes ok. But if the triggers are active, the transaction fails with the message below.

/Users/punkish/Projects/zenodeo3/node_modules/better-sqlite3/lib/methods/transaction.js:66
		after.run();
		      ^
SqliteError: no such savepoint: 	_bs3.
    at sqliteTransaction (/Users/punkish/Projects/zenodeo3/node_modules/better-sqlite3/lib/methods/transaction.js:66:9)
    at Module.<anonymous> (file:///Users/punkish/Projects/zenodeo3/bin/truebug/lib/database/index.js:245:13)
    at Module.sqliteTransaction (/Users/punkish/Projects/zenodeo3/node_modules/better-sqlite3/lib/methods/transaction.js:65:24)
    at processFiles (file:///Users/punkish/Projects/zenodeo3/bin/truebug/index.js:159:26)
    at etl (file:///Users/punkish/Projects/zenodeo3/bin/truebug/index.js:208:9)
    at update (file:///Users/punkish/Projects/zenodeo3/bin/truebug/index.js:251:9)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  code: 'SQLITE_ERROR'
}

If I change the triggers to something innocuous like below, then also the transaction completes ok.

CREATE TRIGGER IF NOT EXISTS tr_afterInsert 
    AFTER INSERT ON treatments 
    BEGIN
        SELECT rowid FROM treatmentsFts WHERE rowid = 0;
    END;

I entered the SQL statements, wrapped in a transaction, on the SQLite CLI, and they work fine even with the triggers. So, this is likely a problem with better-sqlite3 or, more likely, with my code. Can anyone shed some light on what is going on? Perhaps an important note: the above JavaScript code is a transaction that inserts a single "treatment" (see const treatment {} at the top of the post) runs inside another transaction that inserts many treatments like so

const insertTreatments = db.transaction((treatments) => {

        for (const treatment of treatments) {
            insertTreatment(treatment, cache);
        }

});

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions