Using sqlite3 3.43.2 on macOS, I feed the following into an otherwise-empty in-memory database:
.mode markdown
.headers on
create table job(
name text not null,
billable real not null,
check(billable > 0.0) on conflict abort
);
begin transaction;
insert into job values ("calibrate", 1.5);
insert into job values ("reset", -0.5);
insert into job values ("clean", 0.5);
commit;
select * from job;
I expect to get nothing from the final select because there is a check violation in the second insert inside a transaction, but instead the other two insert statements have executed:
Runtime error near line 12: CHECK constraint failed: billable > 0.0 (19)
| name | billable |
|-----------|----------|
| calibrate | 1.5 |
| clean | 0.5 |
- Removing
on conflict abortfrom the table definition does not change this behavior. - Replacing
on conflict abortwithon conflict rollbackin the table definition does not change this behavior.
All right: let's move the error handling to the insert statements. In a fresh in-memory database:
create table job(
name text not null,
billable real not null,
check(billable > 0.0)
);
begin transaction;
insert or rollback into job values ("calibrate", 1.5);
insert or rollback into job values ("reset", -0.5);
insert or rollback into job values ("clean", 0.5);
commit;
select * from job;
The output is:
Runtime error near line 12: CHECK constraint failed: billable > 0.0 (19)
Runtime error near line 14: cannot commit - no transaction is active
| name | billable |
|-------|----------|
| clean | 0.5 |
which shows that SQLite carried on and executed the third statement inside the transaction (which inserted clean) after the error in the second statement in that transaction. I expected that when the error occurred in the second statement, the entire transaction would abort, i.e., SQLite wouldn't even try the third insert. (My mental model is exceptions being raised and caught in programming languages.)
Finally, in yet another fresh database:
insert or rollback into job values ("calibrate", 1.5);
insert or rollback into job values ("reset", -0.5);
insert or rollback into job values ("clean", 0.5);
produces:
Runtime error near line 11: CHECK constraint failed: billable > 0.0 (19)
| name | billable |
|-----------|----------|
| calibrate | 1.5 |
| clean | 0.5 |
which is what I expect, but not what I want.
Is there a way to create a true multi-statement transaction in SQLite? If so, what is the syntax? I want something like:
-- this is not valid SQLite syntax
begin transaction;
insert into job values ("calibrate", 1.5);
insert into job values ("reset", -0.5);
insert into job values ("clean", 0.5);
commit or rollback;
but as noted, that doesn't parse. The desired effect is that if any statement inside the transaction fails, the entire transaction is rolled back.
Is this maybe an alternative acceptable behavior?
edit for clarity; you need to decide how to handle successful statements:
or