sqlite

SQLite sqlite3_last_insert_rowid not incrementing when using INSERT INTO trigger


I have a table with a INTEGER PRIMARY KEY and several FOREIGN KEY fields that point to other tables:

CREATE TABLE DifficultTable 
(
    id     INTEGER NOT NULL UNIQUE,
    name   TEXT,
    fooId  INTEGER,
    barId  INTEGER,

    PRIMARY KEY(id AUTOINCREMENT),
    FOREIGN KEY(fooId) REFERENCES FooTable(id)
    FOREIGN KEY(barId) REFERENCES BarTable(id)
);

CREATE TABLE FooTable
(
    id   INTEGER NOT NULL UNIQUE,
    name TEXT    NOT NULL UNIQUE,

    PRIMARY KEY (id AUTOINCREMENT)
);

CREATE TABLE BarTable
(
    id   INTEGER NOT NULL UNIQUE,
    name TEXT    NOT NULL UNIQUE,

    PRIMARY KEY (id AUTOINCREMENT)
);

To simplify this for the C-API that accesses it, I also provide this data in a VIEW that resolves those foreign keys to the names and I supply a trigger to INSERT:

CREATE VIEW SimpleView 
AS
    SELECT
        DifficultTable.id,
        DifficultTable.name,
        FooTable.name AS foo,
        BarTable.name AS bar
    FROM 
        DifficultTable
    LEFT JOIN 
        FooTable ON DifficultTable.fooId = FooTable.id
    LEFT JOIN 
        BarTable ON DifficultTable.barId = BarTable.id;

CREATE TRIGGER SimpleInsert 
AS
INSTEAD OF INSERT ON SimpleView
BEGIN
    INSERT INTO DifficultTable(name, fooId, barId) 
    VALUES (NEW.name,
            (SELECT id FROM FooTable WHERE name = New.foo),
            (SELECT id FROM BarTable WHERE name = New.bar)
           );
END;

If I insert a few values into FooTable and BarTable, I can confirm sqlite3_last_insert_rowid() is working well:

sqlite> INSERT INTO FooTable(name) VALUES ("One"),("Two"),("Three"),("Four");
sqlite> SELECT last_insert_rowid();
4    <-- Good.  There are now four rows in FooTable.
sqlite> INSERT INTO BarTable(name) VALUES ("One"),("Two");
sqlite> SELECT last_insert_rowid();
2    <-- Good.  There are now two rows in BarTable

But if I insert into SimpleView, the last_insert_rowid() is not updated:

sqlite> INSERT INTO SimpleView (name, foo, bar) VALUES ("One", "Four", "Two");
sqlite> SELECT last_insert_rowid();
2  <-- Expected 1 because there is now 1 row in DifficultTable

Is there an easy way to get that rowid?

Summary of all data in the database after running these commands:

FooTable

id name
1 One
2 Two
3 Three
4 Four

BarTable

id name
1 One
2 Two

SimpleView

id name foo bar
1 One Four Two

DifficultView

id name fooId barId
1 One 4 2

I was hoping to use sqlite3_last_insert_rowid() because it's just a 1-liner in C. I might be able to do another SQL Query, but is it guaranteed that id will always increment (even if rows get deleted?)

SELECT id 
FROM DifficultTable 
ORDER BY id DESC 
LIMIT 1

Solution

  • Here's an answer per Shawn's comments. Last Insert Rowid documentation says:

    If an INSERT occurs within a trigger then this routine will return the rowid of the inserted row as long as the trigger is running. Once the trigger program ends, the value returned by this routine reverts to what it was before the trigger was fired.

    This means you cannot use sqlite3_last_insert_rowid() after completing an INSERT on a trigger. The alternative is performing a query:

    SELECT id 
    FROM DifficultTable 
    ORDER BY id DESC 
    LIMIT 1
    

    This will work fine unless the table fills up because of the description of AUTOINCREMENT:

    The usual algorithm is to give the newly created row a ROWID that is one larger than the largest ROWID in the table prior to the insert. ... If the largest ROWID is equal to the largest possible integer ... the database engine starts picking positive candidate ROWIDs at random until it finds one that is not previously used.

    AUTOINCREMENT isn't necessary. Using this keyword changes the description above to:

    The ROWID chosen for the new row is at least one larger than the largest ROWID that has ever before existed in that same table