When does Cascade Delete Commit in Sqlite? - sqlite

This is my sql set of statements:
char *sql = "BEGIN TRANSACTION;
CREATE TABLE Friends(id varchar UNIQUE);
CREATE TABLE Enemies(id2 varchar ,id3 INTEGER,CONSTRAINT id_econst
FOREIGN KEY(id2) REFERENCES Friends(id) ON DELETE CASCADE);
INSERT INTO Friends VALUES('11');
INSERT INTO Friends VALUES('2');
INSERT INTO Enemies VALUES('11',3);
INSERT INTO Enemies VALUES('3',5);
END TRANSACTION;
PRAGMA foreign_keys = ON;
DELETE FROM Friends WHERE id = '11';";
Performs cascade delete on the Enemies table, whereas :
char *sql = "BEGIN TRANSACTION;
CREATE TABLE Friends(id varchar UNIQUE);
CREATE TABLE Enemies(id2 varchar ,id3 INTEGER,CONSTRAINT id_econst
FOREIGN KEY(id2) REFERENCES Friends(id) ON DELETE CASCADE);
INSERT INTO Friends VALUES('11');
INSERT INTO Friends VALUES('2');
INSERT INTO Enemies VALUES('11',3);
INSERT INTO Enemies VALUES('3',5);
PRAGMA foreign_keys = ON;
DELETE FROM Friends WHERE id = '11';
END TRANSACTION;";
does normal delete.
Why is it so ?

You are enabling foreign key support within the transaction. To quote sqlite:
This pragma is a no-op within a transaction
https://sqlite.org/pragma.html#pragma_foreign_keys
In your second example, the pragma has no effect, and the foreign key constraint is not enforced.

Related

SQLite is not assigning primary keys

I have a simple SQLite database created using sqlite3.exe (code is below). When I run it through SchemaSpy, it looks like there are no primary keys in my first three tables. I don't understand what is wrong with the CREATE TABLE statements in the first three tables.
Code In:
.open test1.db
PRAGMA foreign_keys = ON;
CREATE TABLE META_E (E_ID INTEGER PRIMARY KEY NOT NULL, E_Name TEXT, Region TEXT, Date DATE);
CREATE TABLE META_D (D_ID INTEGER PRIMARY KEY NOT NULL, Citation TEXT, is_used BOOLEAN);
CREATE TABLE META_G (Completion TEXT, D_ID INTEGER NOT NULL, Location TEXT, PRIMARY KEY (D_ID), FOREIGN KEY (D_ID) REFERENCES META_D (D_ID));
CREATE TABLE P_ID_Main (Source_File TEXT, P_ID INTEGER NOT NULL, E_ID INTEGER NOT NULL, D_ID INTEGER NOT NULL, PRIMARY KEY (P_ID, E_ID, D_ID), FOREIGN KEY (E_ID) REFERENCES META_E (E_ID), FOREIGN KEY (D_ID) REFERENCES META_D (D_ID));
.dump
Dump:
sqlite> .dump
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE META_E (E_ID INTEGER PRIMARY KEY NOT NULL, E_Name TEXT, Region TEXT, Date DATE);
CREATE TABLE META_D (D_ID INTEGER PRIMARY KEY NOT NULL, Citation TEXT, is_used BOOLEAN);
CREATE TABLE META_G (Completion TEXT, D_ID INTEGER NOT NULL, Location TEXT, PRIMARY KEY (D_ID), FOREIGN KEY (D_ID) REFERENCES META_D (D_ID));
CREATE TABLE P_ID_Main (Source_File TEXT, P_ID INTEGER NOT NULL, E_ID INTEGER NOT NULL, D_ID INTEGER NOT NULL, PRIMARY KEY (P_ID, E_ID, D_ID), FOREIGN KEY (E_ID) REFERENCES META_E (E_ID), FOREIGN KEY (D_ID) REFERENCES META_D (D_ID));
COMMIT;
sqlite>
SchemaSpy:
I don't understand what is wrong with the CREATE TABLE statements in
the first three tables.
I regard to the indexes there is nothing wrong with the CREATE TABLE statements. Rather it appears to be a shortfall of SchemaSpy.
SQLite isn't creating indexes as there is no need as the columns E_ID, D_ID (for both the META_D and META_G tables) are aliases of the rowid column, which could be considered as the MASTER index (see link below).
In short the column's are in fact indexed but there is no need for SQLite to create an index as the index is built in (an exception is a special type of table a WITHOUT ROWID table).
As such the indexes won't appear in sqlite_master, as there is no need and it appears that SchemaSpy at least the way you have it configured, doesn't fully cater for SQLite.
You may wish to refere to ROWIDs and the INTEGER PRIMARY KEY

Delete row and referencing rows in another table in SQLite 3

I'm using SQLite to play around and learn some more SQL. I have a SQLite 3 database populated like this:
create table playlist (id integer primary key autoincrement, name text);
create table playlistitem (id integer primary key autoincrement,
playlist_id integer, name text);
insert into playlist (name) values ("Moss");
insert into playlist (name) values ("Jen");
insert into playlistitem (playlist_id, name) values (1, "Roy");
insert into playlistitem (playlist_id, name) values (1, "Richmond");
insert into playlistitem (playlist_id, name) values (2, "Denholm");
Great, now I have two playlist items in the "Moss" playlist, "Roy" and "Richmond"; I have one item in the "Jen" playlist: "Denholm".
What I'd like to do is delete the "Moss" playlist and all of its items with a single query.
I saw something like this, which fails for me:
delete playlist, playlistitem from playlist
inner join playlistitem on playlistitem.playlist_id = playlist.id
where playlist.name = "Moss";
Failure:
Error: near "playlist": syntax error
What am I doing wrong?
sqlite doesn't support join in delete statement. You have to use separate query that deletes from second table based on playlist_id, making a delete trigger on playlist, or make that reference a foreign key with on delete cascade:
create table playlistitem (
id integer primary key autoincrement,
playlist_id integer, name text,
foreign key(playlist_id) references playlist(id) on delete cascade);
and then just using delete from playlist where name='Moss'.
Don't forget to enable foreign keys - pragma foreign_keys=1 (you have to re-enable this on each sqlite connection, e.g. as the first command after connecting).

foreign key mismatch on delete command for unrelated record

I have the following 5 tables defined with a few records inserted into the 1st 4. This is using sqlite 3.7.1.7 with foreign key constaint enabled.
create table if not exists subject (id varchar(50) primary key,desc varchar(100));
insert into subject (id,desc) values ("subject1","test subject");
create table if not exists subjectlevel (id_subject_id varchar(50) references subject(id) on delete cascade, id integer not null, desc varchar(100) not null, questmcmaxselections integer not null, primary key (id_subject_id,id));
insert into subjectlevel (id_subject_id,id,desc,questmcmaxselections) values ("subject1",1,"test subject1 level 1",4);
insert into subjectlevel (id_subject_id,id,desc,questmcmaxselections) values ("subject1",2,"test subject1 level 2",4);
create table if not exists questmc (id integer primary key, text varchar(300) not null, includeallanswers int not null, subject_id varchar(50), subjectlevel_id integer, foreign key (subject_id, subjectlevel_id) references subjectlevel (id_subject_id,id) on delete cascade);
insert into questmc (text,includeallanswers,subject_id,subjectlevel_id) values ("this is a _ question", 1, "subject1",1);
create table if not exists questmcselection (id integer primary key, text varchar(100) not null, subject_id varchar(50), subjectlevel_id integer, foreign key (subject_id, subjectlevel_id) references subjectlevel (id_subject_id,id) on delete cascade);
insert into questmcselection (text,subject_id,subjectlevel_id) values ("this is a solution","subject1",1);
create table if not exists questmc_questmcselection(id integer primary key, answer integer not null, questmc_id integer, questmcselection_id integer, subject_id varchar(50), subjectlevel_id integer, foreign key (questmc_id) references questmc(id) on delete cascade, foreign key (questmcselection_id) references questmcselection (id) on delete cascade, foreign key (subject_id,subjectlevel_id) references questmc (subject_id,subjectlevel_id) on delete cascade, foreign key (subject_id,subjectlevel_id) references questmcselection (subject_id,subjectlevel_id));
if i attempt to delete the second record in the subjectlevel table, i get a foreign key mismatch error as long as table questmc_questmcselection is defined.
sqlite> delete from subjectlevel where id=2;
Error: foreign key mismatch - "questmc_questmcselection" referencing "questmcselection"
questmc, questmcselection, and questmc_questmcselection have no related existing records that should prevent this deletion. Any idea why this error occurs?
This error has nothing to do with this particular subjectlevel record.
Your problem is that your tables lack the required indexes.
This was not reported earlier because that DELETE statement was the first command that required SQLite to check the consistency of the database schema.
Based on CL's answer -
sqlite> create table parent(a);
sqlite> create table child(a, FOREIGN KEY (a) REFERENCES parent(a));
sqlite> pragma foreign_keys = ON;
sqlite> insert into parent values(3);
sqlite> insert into child values (3);
Error: foreign key mismatch - "child" referencing "parent"
sqlite> create unique index p_a on parent(a);
sqlite> insert into child values (3);
sqlite> _
From the documentation:
Usually, the parent key of a foreign key constraint is the primary key
of the parent table. If [not], then the parent key columns must be
collectively subject to a UNIQUE constraint or have a UNIQUE index [which uses]
the collation sequences ... in the CREATE TABLE
statement for the parent table.
i.e. the alternative is:
sqlite> create table parent(a, b, UNIQUE (a, b));
sqlite> create table child (x, y, FOREIGN KEY (x, y) REFERENCES parent(a, b));
(this also highlights multi-column foreign keys; they work with indexes too...)

Is it allowed to have null foreign key in sqlite when PRAGMA foreign_keys=ON?

according to this null foreign keys are allowed unless and until we are adding the appropriate "NOT NULL" constraint to the schema.
but I am seeing the some different behavior,
sqlite> PRAGMA Foreign_keys;
1
sqlite> create table proc (
sqlite> pid integer,
sqlite> name text,
sqlite> ppid integer,
sqlite> foreign key (ppid) references proc (id)
sqlite> );
sqlite> .schema proc
CREATE TABLE proc (
pid integer,
name text,
ppid integer,
foreign key (ppid) references proc (id)
);
sqlite> insert into proc (pid, name, ppid)
sqlite> values (0, "init", null);
Error: foreign key mismatch
sqlite> PRAGMA Foreign_keys=OFF;
sqlite> PRAGMA Foreign_keys;
0
sqlite> insert into proc (pid, name, ppid)
sqlite> values (0, "init", null);
sqlite> select * from proc;
0|init|
how can I allow null foreign key in sqlite when PRAGMA foreign_keys=ON? or it is not possible at all?
The ID column is named pid, not id.
The parent key column must have a UNIQUE or PRIMARY KEY constraint.
Try adding a foreign key clause by changing your table create statement to:
CREATE TABLE proc (pid integer, name text, ppid integer, foreign key (ppid) references
proc (id) ON UPDATE CASCADE ON DELETE SET NULL);

how do I override the autoincremented primary key when doing an insert

In MS SQL I would use
SET IDENTITY INSERT ON
How do I do something similar in SQLite. I am trying to upgrade a database and want to maintain the IDs from the original
Thanks
You don't need to set IDENTITY INSERT, because it is always possible to set the value explicitly. With SQLite, you can just insert into the ROWID column:
drop table test;
create table test(name varchar);
insert into test(name) values('Hello');
insert into test(rowid, name) values(10, 'World');
select rowid, name from test;
The same if you use an autoincrement primary key:
drop table test;
create table test(id integer primary key autoincrement, name varchar);
insert into test(name) values('Hello');
insert into test values(10, 'World');
select * from test;
See also http://www.sqlite.org/autoinc.html

Resources