SQLite won't throw error when inserting non-existing foreign key value [duplicate] - sqlite

This question already has answers here:
Does SQLite3 not support foreign key constraints?
(5 answers)
Closed 6 years ago.
I have a litte problem with my SQLite database, especially with the following tables:
CREATE TABLE foo (
key INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE bar (
_id INTEGER PRIMARY KEY,
key INTEGER NOT NULL,
what TEXT NOT NULL,
else TEXT,
FOREIGN KEY (key) REFERENCES foo (key) DEFERRABLE INITIALLY DEFERRED
);
If I understand the SQLite documentation the right way, attempting to insert a row into bar that does not correspond to any row in the foo table should fail and thow an error or something.
Unfortunately the following command works, even if no key 42 existis in foo:
INSERT INTO bar (key, what, else) VALUES (42, "something", "else");
This will create a row in bar with the given values (key = 42), while there exists no row in foo with the key 42.
Is it me or what is wrong here?

As posted by #kijin on Does SQLite3 not support foreign key constraints?
In SQLite 3.x, you have to make the following query every time you connect to an SQLite database:
PRAGMA foreign_keys = ON;
Otherwise SQLite will ignore all foreign key constraints.
Why every time? Backwards compatibility with SQLite 2.x, according to the documentation.
In SQLite 4.x, FK constraints will be enabled by default.

Related

I can't add foriegn key to my existing table. | sqlite3

So i am trying to complete finance. Following is the .schema:
sqlite> .schema
CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, username TEXT NOT NULL, hash TEXT NOT NULL, cash NUMERIC NOT NULL DEFAULT 10000.00);
CREATE TABLE sqlite_sequence(name,seq);
CREATE TABLE history(
symbol TEXT, name TEXT, shares INTEGER, price NUMERIC, time DATETIME
);
CREATE UNIQUE INDEX username ON users (username);
When i try to add foriegn key to history table it always return error. Here is my code:
sqlite> ALTER TABLE history ADD COLUMN id INT;
sqlite> ALTER TABLE history ADD FOREIGN KEY(id) REFRENCES users(id);
Parse error: near "FOREIGN": syntax error
ALTER TABLE history ADD FOREIGN KEY(id) REFRENCES users(id);
^--- error here
I think based on what I see in the sqlite docs that the statement should be together with the ADD column:
ALTER TABLE history ADD COLUMN id INTEGER REFERENCES users(id);
But you please check me on this syntax! Another option is to take care of creating the constraint at the same time that you create the table.
CREATE TABLE history(
symbol TEXT,
name TEXT,
shares INTEGER,
price NUMERIC,
time DATETIME,
id INTEGER,
FOREIGN KEY (id)
REFERENCES users (id));
It might not be something you have realized (yet) but every database has its unique flavor of SQL, so despite there being a SQL standard there are often little differences in the syntax of SQL for specific db implementations. So you always have to beware of this when looking up commands for your sql db.
Further detail on Sqlite foreign key constraints can be found here:
https://www.sqlitetutorial.net/sqlite-foreign-key/

SQlite allows auto increment creation of a primary key referencing another table

I have those two tables implementing some inheritance relationship via the Class Table Inheritance pattern:
pragma foreign_keys = ON;
create table foo(foo_id integer primary key);
create table bar(foo_id integer primary key references foo(foo_id));
Let's populate foo:
insert into foo values (1), (3), (4);
Now I can insert 3 into bar:
insert into bar values(3); -- no error
I cannot insert 2:
insert into bar values(2); -- Error: FOREIGN KEY constraint failed
However, what suprises me is that NULL values can be used to generate new keys:
insert into bar values(NULL); -- OK, 4 inserted
insert into bar values(NULL); -- FOREIGN KEY constraint failed
This behavior seems rather odd. When I try the same thing in MySQL, I am greeted with a
ERROR 1048 (23000): Column 'foo_id' cannot be null
which is what I would expect.
I find this behavior particularly dangerous when inserting new rows in bar with a subquery:
insert into bar (foo_id) values ((select foo_if from foo where ...))
which could end up silently inserting random rows into bar when there is no match in foo, instead of returning an error.
Is this behavior compliant with the SQL standard, in what scenario could it be useful, and more importantly, is there a way this behavior could be changed to match MySQL's?
EDIT
Here is an illustration of the problem in a perhaps more striking (and scary) fashion:
pragma foreign_keys = ON;
create table people(people_id integer primary key, name text not null);
insert into people (name) values ("Mom"), ("Jack the Ripper");
create table family_member(people_id integer primary key references people(people_id));
insert into family_member values ((select people_id from people where name = "Mom"));
insert into family_member values ((select people_id from people where name = "Dad")); -- silent error here
select name from family_member inner join people using (people_id);
-- uh-oh, Jack the Ripper is now part of my family
So I found an anwer to this problem, which may be a bit surprising for people not familiar with SQLite.
It turns out that a column that is declared integer primary key is automatically filled with an unused integer if it is not given a value.
A somewhat arcane way around this in SQLite is to use declare the column int primary key instead, which prevents the implicit automatic handling.
However, we are not over yet, because another distinctive behavior of SQLite is to allow NULL primary keys -- in our example above, insert into bar values (NULL) would insert a new row with a NULL primary key.
To explicitely ban primary keys from having NULL values, it must be declared not null:
pragma foreign_keys = ON;
create table foo(foo_id integer primary key);
create table bar(foo_id int primary key not null references foo(foo_id));
insert into foo values (1), (3), (4);
insert into bar values (3);
insert into bar values (2); -- Error: FOREIGN KEY constraint failed
insert into bar values (NULL); -- Error: NOT NULL constraint failed: bar.foo_id
This is probably the way primary key pointing to external keys should be declared by default in SQLite.

Foreign key constraint does not apply

I have defined a foreign key. To check it, i insert wrong values to the table that has the foreign key. No error were printed and the values were added succefully. I do not know if i am running some old version of sqlite3 or something like that, i am completely new to this area.
create table ref(value1 int ,value2,primary key(value1));
create table for(value1 int,value3 int,primary key(value3),foreign key(value1)references ref(value1));
insert into for values (1,1);
This was added succefully.
Foreign key constraints are disabled by default as it is explained here: SQLite Foreign Key Support.
To enable them execute first:
PRAGMA foreign_keys = ON;
See the demo.

How to get the names of foreign key constraints in SQLite?

Does SQLite indeed have a limitation that it is not possible to retrieve the name of a foreign key? I am asking because I couldn't find this limitation mentioned anywhere in their documentation.
For example, I run the following script:
CREATE TABLE
users (
id INTEGER NOT NULL PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL
) ;
CREATE TABLE
orders (
id INTEGER NOT NULL PRIMARY KEY,
user_id INTEGER NOT NULL,
CONSTRAINT fk_users FOREIGN KEY (user_id) REFERENCES users(id)
) ;
Now I would like to check that the key "fk_users" was created indeed, so I run the following PRAGMA:
PRAGMA foreign_key_list(orders);
I would expect to see the name of my foreign key in the first column, but I am seeing some "0" value instead. Moreover, if I create multiple foreign keys with custom names, they are all called either "0" or "1".
Is this indeed a limitation of SQLite, or am I missing something?
There is no mechanism to extract the constraint name.
The table sqlite_master stores a CREATE command in the column "sql". You could query that command and do some parsing to extract the name of the foreign key. An example for a combined foreign key that works for me:
SELECT sql FROM sqlite_master WHERE name = 'song'
yields
CREATE TABLE "song" (
"songid" INTEGER,
"songartist" TEXT,
"songalbum" TEXT,
"songname" TEXT,
CONSTRAINT "fk__song_album" FOREIGN KEY ("songartist", "songalbum") REFERENCES "album" ("albumartist", "albumname")
)
and contains the name "fk__song_album" of the foreign key.
If one alters the foreign key with a query, the content of the sql column is modified/updated:
The text in the sqlite_master.sql column is a copy of the original CREATE statement text that created the object, except normalized as described above and as modified by subsequent ALTER TABLE statements. The sqlite_master.sql is NULL for the internal indexes that are automatically created by UNIQUE or PRIMARY KEY constraints.
https://www.sqlite.org/fileformat2.html
Extra tip:
In order to see the foreign key information in Navicat (Lite) ... right click on a table and choose "Design table". Then select the foreign keys tab.

SQLite Foreign keys not working correctly [duplicate]

This question already has answers here:
Does SQLite3 not support foreign key constraints?
(5 answers)
Closed 11 months ago.
The following is a query for creating a table:
CREATE TABLE "FacilityRating"(
"FacilityRatingId" INTEGER PRIMARY KEY NOT NULL,
"Stars" VARCHAR,
"Facility_FacilityId" INTEGER,
"User_UserId" INTEGER,
FOREIGN KEY (Facility_FacilityId)
REFERENCES Facility(FacilityId)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (User_UserId)
REFERENCES User(UserId)
ON DELETE CASCADE
ON UPDATE CASCADE
)
However, when I insert a new row in Facility_FacilityId and User_UserId with some random numbers, SQLite does not give error but adds it anyway.
Here is a snapshot:
Any hint what is going on here? I am using SQLite Manager, an Add-on for Mozilla Firefox
The documentation says:
Foreign key constraints must be enabled by the application at runtime, using the PRAGMA foreign_keys command. For example:
sqlite> PRAGMA foreign_keys = ON;
Foreign key constraints are disabled by default (for backwards compatibility), so must be enabled separately for each database connection.
#Override
public void onConfigure(SQLiteDatabase db) {
super.onConfigure(db);
db.setForeignKeyConstraintsEnabled(true);
}

Resources