crawshaw / sqlite

Go SQLite3 driver

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Cascading foreign keys are not working

alinz opened this issue · comments

@crawshaw, Thanks for this library,

I've been trying to get the delete cascading to work but I have no success. I have prepared an working example which can be demonstrate the issue. This code will create 2 tables, names and nicknames. nicknames table has a constraint foreign key to user.id. If a user is deleted, I expect the nicknames' rows related to that user also being deleted. unfortunately user gets deleted, but nicknames table stays the same.

Am I missing a configuration or this is a bug with this library?

Thanks

package main

import (
	"context"

	"crawshaw.io/sqlite/sqlitex"
)

func main() {
	dbpool, err := sqlitex.Open("file:./sample.db", 0, 10)
	if err != nil {
		panic(err)
	}

	defer dbpool.Close()

	err = func() error {
		conn := dbpool.Get(context.Background())
		defer dbpool.Put(conn)

		return sqlitex.ExecScript(conn, `
		PRAGMA foreign_keys = ON;

		CREATE TABLE IF NOT EXISTS users (
			id TEXT PRIMARY KEY,
			name TEXT
		);
		
		CREATE TABLE IF NOT EXISTS nicknames (
			user_id TEXT PRIMARY KEY,
			name TEXT,
		
			CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
		);
		
		INSERT OR REPLACE INTO users (id, name) VALUES ('1', 'ali');
		INSERT OR REPLACE INTO nicknames (user_id, name) VALUES ('1', 'alinz');		
		`)
	}()

	if err != nil {
		panic(err)
	}

	err = func() error {
		conn := dbpool.Get(context.Background())
		defer dbpool.Put(conn)

		return sqlitex.ExecScript(conn, `
		PRAGMA foreign_keys = ON;

		DELETE from users WHERE id = '1';
		`)
	}()

	if err != nil {
		panic(err)
	}
}

So this issue is not so much an issue with this Go package so much as a subtlety to both PRAGMA foreign_keys = ON and sqlitex.ExecScript.

First understand that sqlitex.ExecScript wraps the entire script in a SAVEPOINT.

You are running PRAGMA foreign_keys = ON within this SAVEPOINT, and as such it will have no effect. This is a quirk of sqlite3 that is not well documented, but you can try it for yourself.

PRAGMA foreign_keys;
0
SAVEPOINT "a";
PRAGMA foreign_keys=ON;
PRAGMA foreign_keys;
0
COMMIT;
PRAGMA foreign_keys;
0
PRAGMA foreign_keys=ON;
PRAGMA foreign_keys;
1

You can address this by running PRAGMA foreign_keys = ON using sqlitex.Exec instead. You must do this once on each connection in the sqlitex.Pool each time you start the program or open a new connection.

	const PoolSize = 10
	dbpool, err := sqlitex.Open("file:./sample.db", 0, PoolSize)
	if err != nil {
		panic(err)
	}
	for i := 0; i < PoolSize; i++ {
		conn := dbpool.Get(nil)
		err := sqlitex.Exec(conn, `PRAGMA foreign_keys = ON;`, nil)
		if err != nil {
			panic(err)
		}
	}

Thanks for the explanation.

Note that once you set the pragma on all connections in the pool, that setting will remain on the connections and so there is no need to run it again as you pull connections of the pool later.