Non-existent foreign key doesn't cause error in SQLite - sqlite

The following code should IMO produce an error because the user_id=1 doesn't exist. Why does it work?
CREATE TABLE users (
user_id int PRIMARY KEY,
email text UNIQUE
);
CREATE TABLE claimed (
account_id int PRIMARY KEY,
user_id int,
domain text,
FOREIGN KEY (user_id) REFERENCES users (user_id)
);
INSERT INTO claimed
(user_id, domain)
VALUES (1, "abcd");

From the relevant documentation:
In order to use foreign key constraints in SQLite, the library must be compiled with neither SQLITE_OMIT_FOREIGN_KEY or SQLITE_OMIT_TRIGGER defined. If SQLITE_OMIT_TRIGGER is defined but SQLITE_OMIT_FOREIGN_KEY is not, then SQLite behaves as it did prior to version 3.6.19 (2009-10-14) - foreign key definitions are parsed and may be queried using PRAGMA foreign_key_list, but foreign key constraints are not enforced.
and:
Foreign key constraints are disabled by default (for backwards compatibility), so must be enabled separately for each database connection.
and:
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;
Apparently you should use PRAGMA foreign_keys = ON at the top of your connection, and potentially rebuild with the appropriate options (though if you installed from package then I would personally assume that this has been done).
Source: Google sqlite foreign key, first result

Related

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.

The child's keys don't get deleted when I delete the parent key

I made a database via SQLite which looks like the following:
and I created a table called as books like this:
CREATE TABLE books (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "title-book" TEXT, witer TEXT, comment TEXT, "intro-img" TEXT, "avalable-count" integer, "total-count" integer, "publisher-id" INTEGER REFERENCES publisher (id) ON DELETE CASCADE ON UPDATE CASCADE, "category-id" INTEGER REFERENCES categories (id) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY ("publisher-id") REFERENCES publisher (id) ON DELETE CASCADE ON UPDATE CASCADE);
When I delete a category that is a parent key for books category-id then I see books table having the child key rows that include that category didn't remove and they exist as they were like in the following pictures:
SQLite 3.6.19 and older doesn't support Foreign Keys.
If you have a newer version you can check the support using:
PRAGMA foreign_keys
If it returns "no data" it means there is no support installed (because it was compiled with SQLITE_OMIT_FOREIGN_KEY or SQLITE_OMIT_TRIGGER defined), while returning 0 means "supported but disabled" (default) and 1 means "supported and enabled".
You can enable at runtime using
PRAGMA foreign_keys = ON;
Be aware this must be enabled for every database connection:
Foreign key constraints are disabled by default (for backwards compatibility), so must be enabled separately for each database connection.
Also consider:
It is not possible to enable or disable foreign key constraints in the middle of a multi-statement transaction (when SQLite is not in autocommit mode). Attempting to do so does not return an error; it simply has no effect.
REFERENCE: https://sqlite.org/foreignkeys.html

Does SQLite really not preserve data integrity of foreign key constraints by default?

Newer versions of SQLite support foreign key constraints. It is possible to define
CREATE TABLE MASTER (_ID INTEGER PRIMARY KEY, ...);
CREATE TABLE SERVANT (_ID INTEGER PRIMARY KEY, MASTERID INTEGER,
FOREIGN KEY(MASTERID) REFERENCES MASTER(_ID);
According to the documentation by default "NO ACTION" is used for ON DELETE and ON UPDATE. But contrary to other DBS "NO ACTION" seems not to mean, that delete or update is not performed. It seems to mean that nothing is done to preserve integrity, at least according to my tests(*) and if I understand the documentation right:
Configuring "NO ACTION" means just that: when a parent key is modified
or deleted from the database, no special action is taken.
Thus
INSERT INTO MASTER (_ID) VALUES (1);
INSERT INTO SERVANT (_ID, MASTERID) VALUES (1,1);
DELETE FROM MASTER;
gives me an empty MASTER table and a SERVANT table with a foreign key pointing into nowhere.
Can anyone confirm this behaviour and maybe explain why it is implemented that way? Or do I have to configure something to make foreign key support work?
I am new to SQLite development, so please foregive me, if this is a stupid question.
Edit: (*) my tests were flawed, see my answer below.
I will try to give an answer myself:
No, if configured right, SQLite preserves data integrity in this situation. "NO ACTION" is used by default and this prohibits deletion or update of a master key if there is still a refering key from an referencing table (tested with 3.7.x).
My fault was that I was not aware that PRAGMA foreign_keys = ON; must be configured for every new connection to the database.
Edit: I think the SQLite documentation is misleading here.
You are correct. "NO ACTION" means that nothing is done to preserve the integrity of the foreign key constraints. See the documentation for details on options you can set.
There are 4 other options you can set in this scenario. RESTRICT, SET NULL, SET DEFAULT and CASCADE. A brief description of what they do:
RESTRICT - A row in the MASTER table can only be deleted if it is not referenced by any rows in the SERVANT table.
SET NULL - Deleting a row in the MASTER table will cause any FKs in the SERVANT table to be set to NULL.
SET DEFAULT - Similar to set NULL except that the FK is set to the default value instead of NULL.
CASCADE - Deleting a row in the MASTER table will cause any rows in the SERVANT table that reference the deleted MASTER row to also be deleted.
To change these options, you will have to modify your create statements to specify the on update and on delete actions.
CREATE TABLE MASTER (
_ID INTEGER PRIMARY KEY,
...
);
CREATE TABLE SERVANT (
_ID INTEGER PRIMARY KEY,
MASTERID INTEGER,
FOREIGN KEY(MASTERID) REFERENCES MASTER(_ID) ON UPDATE CASCADE ON DELETE SET NULL
);
edit:
Don't forget to ensure that your version of SQLite was compiled with foreign key support and that you've enabled it by specifying PRAGMA foreign_keys = ON;

SQLite many-to-many relationship?

I'm trying to set up a SQLite3 database with foos and bars and a many-to-many relation between them. This is what I've got so far:
CREATE TABLE foo(
id INTEGER PRIMARY KEY NOT NULL,
foo_col INTEGER NOT NULL
);
CREATE TABLE bar(
id INTEGER PRIMARY KEY NOT NULL,
bar_col TEXT NOT NULL
);
CREATE TABLE foobar(
foo_id INTEGER,
bar_id INTEGER,
FOREIGN KEY(foo_id) REFERENCES foo(id) ON DELETE CASCADE,
FOREIGN KEY(bar_id) REFERENCES bar(id) ON DELETE CASCADE
);
CREATE INDEX fooindex ON foobar(foo_id);
CREATE INDEX tagindex ON foobar(tag_id);
...but it doesn't seem to be working. I can delete a row from foo and it doesn't affect foobar. What am I doing wrong?
Taken from this site, http://www.sqlite.org/foreignkeys.html.
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;
Foreign key constraints are disabled by default (for backwards compatibility), so must be enabled separately for each database connection separately. (Note, however, that future releases of SQLite might change so that foreign key constraints enabled by default. Careful developers will not make any assumptions about whether or not foreign keys are enabled by default but will instead enable or disable them as necessary.) The application can can also use a PRAGMA foreign_keys statement to determine if foreign keys are currently enabled. The following command-line session demonstrates this:
sqlite> PRAGMA foreign_keys;
0
sqlite> PRAGMA foreign_keys = ON;
sqlite> PRAGMA foreign_keys;
1
sqlite> PRAGMA foreign_keys = OFF;
sqlite> PRAGMA foreign_keys;
0
Tip: If the command "PRAGMA foreign_keys" returns no data instead of a single row containing "0" or "1", then the version of SQLite you are using does not support foreign keys (either because it is older than 3.6.19 or because it was compiled with SQLITE_OMIT_FOREIGN_KEY or SQLITE_OMIT_TRIGGER defined).
It is not possible to enable or disable foreign key constraints in the middle of a multi-statement transaction (when SQLite is not in autocommit mode). Attempting to do so does not return an error; it simply has no effect.

Does a SQLite Foreign key automatically have an index?

I know that SQLite does not enforce foreign keys natively, but that's not my primary concern. The question is: If I declare
CREATE TABLE invoice (
invoiceID INTEGER PRIMARY KEY,
clientID INTEGER REFERENCES client(clientID),
...
)
will sqlite at least use the information that clientID is a foreign key to optimize queries and automatically index invoice.clientID, or is this constraint a real no-op?
In the SQLite Documentation it says:
... "an index should be created on the child key columns of each foreign key constraint"
ie. the index is not automatically created, but you should create one in every instance.
Even if it is not actually a no-op (a data structure describing the constraint is added to the table), foreign key related statement doesn't create any index on involved columns.
Indexes are implicitly created only in the case of PRIMARY KEY and UNIQUE statements.
For more details, check it out build.c module on the sqlite source tree:
http://www.sqlite.org/cvstrac/rlog?f=sqlite/src/build.c https://www.sqlite.org/src/file?name=src/build.c&ci=tip

Resources