Foreign key constraint does not apply - sqlite

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.

Related

SQLiteException no such table: main.*_temp

I have a Xamarin.Forms app that uses a SQLite database locally on the device. Here's some sample data structure:
Table x: id, name
Table y: id, name
Table x_y: id, x_id, y_id
Since SQLite doesn't support altering columns, one of the schema updates we sent down in a patch did the following:
Rename table x to x_temp
Create new/updated table x
Insert all data from table x_temp into table x
Drop table if exists x
That seems to work just fine. However, when I'm attempting to run an insert statement on table x_y, I am getting a SQLite exception: "no such table: main.x_temp".
When I look at the SQLite query string while debugging there is no mention of table x_temp whatsoever. So, if I delete the entire database and re-create everything the insert works just fine.
I'm from a MSSQL background, am I not understanding something about SQLite in general? Is the foreign key constraint from table x_y trying to reference x_temp because I renamed the original table (I may have just answered my own question)? If that's the case, surely there is a way around this without having to cascade and re-create every table?
Any input would be appreciated. Thanks!
I believe that your issue may be related to the SQlite version in conjunction with whether or not Foreign Key Support has been turned on.
That is the likliehood is that :-
Is the foreign key constraint from table x_y trying to reference
x_temp because I renamed the original table (I may have just answered
my own question)?
Would be the issue, as you likely have Foreign Key Support turned on as per :-
Prior to version 3.26.0 (2018-12-01), FOREIGN KEY references to a table that is renamed were only edited if the PRAGMA foreign_keys=ON, or in other words if foreign key constraints were begin enforced.
With PRAGMA foreign_keys=OFF, FOREIGN KEY constraints would not be changed when the table that the foreign key referred to (the "parent table") was renamed.
Beginning with version 3.26.0, FOREIGN KEY constraints are always converted when a table is renamed, unless the PRAGMA legacy_alter_table=ON setting is engaged. The following table summaries the difference:
SQL As Understood By SQLite - ALTER TABLE
If that's the case, surely there is a way around this without having
to cascade and re-create every table?
Yes, as the latest version of SQlite on Android is 3.19.0 (I believe), then you can turn Foreign Key support off using the foreign_keys pragma when renaming the table.
Note Foreign Keys cannot be turned off within a transaction.
See SQL As Understood By SQLite - ALTER TABLE and PRAGMA foreign_keys = boolean;

Non-existent foreign key doesn't cause error in 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

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);
}

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.

Resources