JDBC ignores SQLite foreign key constraint ON DELETE action - sqlite

I've a Rate table with the following structre:
CREATE TABLE Rate (
id INTEGER PRIMARY KEY AUTOINCREMENT,
book_id INTEGER,
value INTEGER,
user_id INTEGER,
FOREIGN KEY(user_id) REFERENCES User(ID),
FOREIGN KEY(book_id) REFERENCES book(ID) ON DELETE RESTRICT
)
And a book table with the following structure:
DROP TABLE IF EXISTS Book;
CREATE TABLE Book (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
ISBN VARCHAR,
author_id INTEGER,
editor_id INTEGER,
translator_id INTEGER,
publisher_id INTEGER,
"type" VARCHAR,
language VARCHAR,
"date" VARCHAR,
format VARCHAR,
summary TEXT,
FOREIGN KEY(author_id) REFERENCES Author(ID) ON DELETE SET NULL ON UPDATE CASCADE,
FOREIGN KEY(editor_id) REFERENCES Editor(ID) ON DELETE SET NULL ON UPDATE CASCADE,
FOREIGN KEY(translator_id) REFERENCES Translator(ID) ON DELETE SET NULL ON UPDATE CASCADE,
FOREIGN KEY(publisher_id) REFERENCES Publisher(ID) ON DELETE SET NULL ON UPDATE CASCADE
);
So, if I have a Rate entry like this:
1 1 4 3(ID, BOOK_ID, VALUE, USER_ID)
I shouldn't be able to delete the Book with the ID 1, right?
This is exactly what happens when I try to delete the Book with ID 1 on SQLITE Manager. It gives me
FOREIGN KEY constraint failed
However, when I call my delete from the code, it totally ignores the restriction and deletes the book and there's no change in the Rate entry it is still:
1 1 4 3
And my delete method is like the following:
public void delete() {
try {
String query = "DELETE FROM book WHERE id = ? ";
PreparedStatement statement = db.prepareStatement(query);
statement.setInt(1, (Integer) this.id);
statement.executeUpdate();
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
When i run PRAGMA foreign_keys on SQLITE Manager it returns 1. So, I assume that the database is created correctly.

The documentation says:
... foreign key constraints ... must still be enabled by the application at runtime, using the PRAGMA foreign_keys command. For example:
PRAGMA foreign_keys = ON;
Foreign key constraints are disabled by default (for backwards compatibility), so must be enabled separately for each database connection.

Related

Strange org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (foreign key mismatch -

I'm running into org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (foreign key mismatch - ... with a statement, that proceeds without any complaints using the normal SQLite-frontend. This creates the crucial part of my database:
CREATE TABLE IF NOT EXISTS artists (
aid INTEGER PRIMARY KEY AUTOINCREMENT,
aname VARCHAR(200) NOT NULL,
CONSTRAINT one UNIQUE(aname)
);
CREATE TABLE IF NOT EXISTS discs (
did INTEGER PRIMARY KEY AUTOINCREMENT,
testAddCD1 BIGINT(10) NOT NULL,
dtitle VARCHAR(125) NOT NULL,
dreleaseyear YEAR(4) DEFAULT NULL,
dlang VARCHAR(3) DEFAULT NULL
);
CREATE TABLE IF NOT EXISTS tracks (
discs_did INTEGER NOT NULL,
tnumber INT(4) NOT NULL,
ttitle VARCHAR(125) NOT NULL,
tseconds INT(4) NOT NULL,
CONSTRAINT pk PRIMARY KEY(discs_did, tnumber),
CONSTRAINT fk FOREIGN KEY(discs_did) REFERENCES discs(did) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT val CHECK(tseconds> 0)
);
CREATE TABLE IF NOT EXISTS track_by_artist (
discs_did INTEGER NOT NULL,
tracks_tnumber INT(4) NOT NULL,
artists_aid INTEGER NOT NULL,
CONSTRAINT pk PRIMARY KEY(discs_did, tracks_tnumber, artists_aid),
CONSTRAINT fk1 FOREIGN KEY(discs_did) REFERENCES discs(did) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT fk2 FOREIGN KEY(tracks_tnumber) REFERENCES tracks(tnumber) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT fk3 FOREIGN KEY(artists_aid) REFERENCES artists(aid) ON DELETE RESTRICT ON UPDATE RESTRICT
The database gets created and the JDBC-driver inserts an artist, a disc and the disc's tracks - all good. The final insert should assign an artist to a track and looks like
INSERT INTO track_by_artist (discs_did, tracks_tnumber, artists_aid) VALUES (1, 1, 1);
Using the JDBC this yields
SQLite-Error #1
org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (foreign key mismatch - "track_by_artist" referencing "tracks")
at org.sqlite.core.DB.newSQLException(DB.java:1012)
at org.sqlite.core.DB.newSQLException(DB.java:1024)
at org.sqlite.core.DB.throwex(DB.java:989)
at org.sqlite.core.NativeDB.prepare_utf8(Native Method)
at org.sqlite.core.NativeDB.prepare(NativeDB.java:134)
at org.sqlite.core.DB.prepare(DB.java:257)
at org.sqlite.core.CorePreparedStatement.<init>(CorePreparedStatement.java:45)
at org.sqlite.jdbc3.JDBC3PreparedStatement.<init>(JDBC3PreparedStatement.java:30)
at org.sqlite.jdbc4.JDBC4PreparedStatement.<init>(JDBC4PreparedStatement.java:25)
at org.sqlite.jdbc4.JDBC4Connection.prepareStatement(JDBC4Connection.java:35)
at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:241)
at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:205)
Issuing the same SQL-Insert with SQLite's text-frontend works like cream.
I'm a little lost and don't know what to do about my Java-code.
Some advise, pls?
Chris
The problem is that in track_by_artist you defined this foreign key constraint:
CONSTRAINT fk2 FOREIGN KEY(tracks_tnumber) REFERENCES tracks(tnumber) ON DELETE RESTRICT ON UPDATE RESTRICT
although tnumber in tracks is not UNIQUE (and it shouldn't be).
A foreign key's parent must be defined as UNIQUE.
In tracks the PRIMARY KEY is defined as the combination of discs_did and tnumber, which makes sense, so the combination of these 2 columns is unique.
What you can do is define in track_by_artist a composite foreign key for the columns discs_did and tracks_tnumber that reference discs_did and tnumber of tracks:
CREATE TABLE IF NOT EXISTS track_by_artist (
discs_did INTEGER NOT NULL,
tracks_tnumber INT(4) NOT NULL,
artists_aid INTEGER NOT NULL,
CONSTRAINT pk PRIMARY KEY(discs_did, tracks_tnumber, artists_aid),
CONSTRAINT fk1 FOREIGN KEY(discs_did, tracks_tnumber) REFERENCES tracks(discs_did, tnumber) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT fk2 FOREIGN KEY(artists_aid) REFERENCES artists(aid) ON DELETE RESTRICT ON UPDATE RESTRICT
);
This way you don't need a separate foreign key definition for discs_did.

Sqlite3: Error deleting record: foreign key mismatch

I am trying to delete a record from table users.
Tried deleteing it with a DELETE-statement (DELETE FROM users WHERE user_id=10" as well as in my DB browser, but I get the above error, specifically it says: "Error deleting record: foreign key mismatch - "games" referencing "groups" (DELETE FROM "main"."users" WHERE rowid IN ('10');)".
Below my schema:
CREATE TABLE users (
user_id INTEGER PRIMARY KEY,
name VARCHAR(255) NOT NULL,
hash VARCHAR(255) NOT NULL,
UNIQUE(name));
CREATE TABLE 'groups' (
'group_name' VARCHAR(255) NOT NULL,
'turn' INTEGER NOT NULL,
'user_id'INTEGER,
FOREIGN KEY ('user_id') REFERENCES 'users'('user_id')
ON UPDATE CASCADE
ON DELETE CASCADE
);
CREATE TABLE games (
game_id INTEGER PRIMARY KEY,
active INTEGER,
turn INTEGER,
group_name VARCHAR(255) NOT NULL,
FOREIGN KEY (turn) REFERENCES groups(turn_id)
ON UPDATE CASCADE
ON DELETE CASCADE,
FOREIGN KEY (group_name) REFERENCES groups(group_name)
ON UPDATE CASCADE
ON DELETE NO ACTION
);
Why is this a problem? user_id is not even a foreign key in 'games'?? Thank you!
The problem in the end was my DB Browser for SQLite (3.11.2) whose GUI allows deletions but they don't actually work. When I tied again in the bash and closed and restarted the DB Browser, the rows were gone. Uninsightful but figured I'd post nonetheless in case anyone else comes across this.

What is causing this sqlite foreign key mismatch?

I already checked out this question, and thought I had the answer - but then it didn't look right to me.
I have the following pared down example:
CREATE TABLE pipelines (
name VARCHAR NOT NULL,
owner VARCHAR NOT NULL,
description VARCHAR,
PRIMARY KEY (name, owner),
FOREIGN KEY(owner) REFERENCES user (id)
);
CREATE TABLE tasks (
id INTEGER NOT NULL,
title VARCHAR,
pipeline VARCHAR,
owner VARCHAR,
PRIMARY KEY (id),
FOREIGN KEY(pipeline) REFERENCES pipelines (name),
FOREIGN KEY(owner) REFERENCES pipelines (owner)
);
CREATE TABLE user (
id VARCHAR NOT NULL,
name VARCHAR,
password VARCHAR,
PRIMARY KEY (id)
);
pragma foreign_keys=on;
insert into user values ('wayne', '', '');
insert into pipelines values ('pipey', 'wayne', '');
insert into tasks values (1, 'hello', 'pipey', 'wayne');
When executing this code, it bails out:
$ sqlite3 foo.sq3 '.read mismatch.sql'
Error: near line 27: foreign key mismatch
Through the list in the question I cited:
the parent table (user) exists.
the parent columns (name, owner) exist
the parent columns are, in fact, the primary key (I thought that may have been it originally)
the child table references all of the primary key columns in the parent table
So what in the world could be causing this error?
The documentation says:
Usually, the parent key of a foreign key constraint is the primary key of the parent table. If they are not the primary key, then the parent key columns must be collectively subject to a UNIQUE constraint or have a UNIQUE index.
In the pipelines table, neither the name nor the owner columns are, by themselves, unique.
I guess you actually want to have a two-column foreign key in the tasks table:
FOREIGN KEY(pipeline, owner) REFERENCES pipelines(name, owner)

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...)

Foreign Key constraint doesn't work

I have two tables, theme and quiz, here is their definition:
CREATE TABLE "theme" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , "nom" VARCHAR NOT NULL );
CREATE TABLE quiz(
id INTEGER PRIMARY KEY,
nom VARCHAR(256) NOT NULL,
theme INTEGER NOT NULL,
niveau INTEGER NOT NULL,
pass INTEGER DEFAULT 1 NOT NULL,
jok INTEGER DEFAULT 1 NOT NULL,
etat INTEGER DEFAULT 0 NOT NULL,
FOREIGN KEY (theme) REFERENCES theme(id)
);
The field id (the primary key) in the table theme is a Foreign Key in the quiz table.
When i try to insert a record in the table quiz which contain the value 30 for example as a foreign key, the record is inserted successfully in the quiz table although there is no record in the theme table with the id = 30, i mean, wasn't supposed to interdict this insert since i had a Foreign key constraint?
Are you sure foreign key support is enabled?
Assuming the library is compiled with foreign key constraints enabled,
it must still be enabled by the application at runtime, using the
PRAGMA foreign_keys command. For example:
sqlite> PRAGMA foreign_keys = ON;

Resources