-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Foreign key constraint is only working with Exec() method without RETURNING clause #986
Comments
I am unable to reproduce the issue. The following test program gets back a "FOREIGN KEY constraint failed" error from the call to package main
import (
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
)
func main() {
c, err := sql.Open("sqlite3", "file::memory:?_foreign_keys=1")
if err != nil {
panic(err)
}
defer c.Close()
c.SetConnMaxIdleTime(0)
c.SetConnMaxLifetime(0)
c.SetMaxIdleConns(1)
c.SetMaxOpenConns(1)
if err := c.Ping(); err != nil {
panic(err)
}
if _, err := c.Exec(`CREATE TABLE Parent(id INTEGER PRIMARY KEY)`); err != nil {
panic(err)
}
if _, err := c.Exec(`CREATE TABLE Child(id INTEGER PRIMARY KEY, parent_id INTEGER NOT NULL REFERENCES Parent(id))`); err != nil {
panic(err)
}
row := c.QueryRow(`INSERT INTO child (parent_id) VALUES (666) RETURNING id`)
var id uint32
if err := row.Scan(&id); err != nil {
panic(err)
}
fmt.Println(id)
} Please share a dump of the SQLite schema that gorm is creating for you. To do so, generate your database to a file rather than in-memory, and then use the |
@rittneje thank you for response. It's probably "intended" for now, since SQLite console works exactly same way: sqlite> PRAGMA foreign_keys(1);
sqlite> CREATE TABLE Parent(id INTEGER PRIMARY KEY);
sqlite> CREATE TABLE Child(id INTEGER PRIMARY KEY, parent_id INTEGER NOT NULL REFERENCES Parent(id));
sqlite> INSERT INTO child (parent_id) VALUES (666) RETURNING id;
1. // <--- generated ID returned
Error: FOREIGN KEY constraint failed // <---- error afterwards package main
import (
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
)
func main() {
c, err := sql.Open("sqlite3", "file::memory:?_foreign_keys=1&_pragma=foreign_keys(1)")
if err != nil {
panic(err)
}
defer c.Close()
c.SetConnMaxIdleTime(0)
c.SetConnMaxLifetime(0)
c.SetMaxIdleConns(1)
c.SetMaxOpenConns(1)
if err := c.Ping(); err != nil {
panic(err)
}
if _, err := c.Exec(`CREATE TABLE Parent(id INTEGER PRIMARY KEY)`); err != nil {
panic(err)
}
if _, err := c.Exec(`CREATE TABLE Child(id INTEGER PRIMARY KEY, parent_id INTEGER NOT NULL REFERENCES Parent(id))`); err != nil {
panic(err)
}
rows, err := c.Query(`INSERT INTO child (parent_id) VALUES (666) RETURNING id`)
if err != nil {
panic((err))
}
var id uint32
for rows.Next() {
if err := rows.Scan(&id); err != nil {
panic(err)
}
fmt.Printf("scanned %v\n", id) // <----- successfully scanned generated ID
}
if rows.Err() != nil {
panic(rows.Err()) // <----- only at this stage can we spot foreign key violation
}
} |
Hi! I won't be able to offer much insight into the details of how Postgres and SQLite prepare and check statements and when they fail, etc. to explain the initial discrepancies you pointed out, however I'd like to clarify how the golang You should use https://pkg.go.dev/database/sql#Rows.Next
For statements that will return a single row, like in your examples above, you can also use
will return the expected error. |
I did some more testing, and it seems that what is actually happening is the constraint violation isn't being detected until the underlying call to According to the SQLite docs this seems to be one of the known flaws of the original implementation, which allegedly was fixed. So maybe it'll work more reasonably in 3.37.0? (The library currently vendors version 3.36.0.) |
I tested on 3.37 and observed same behavior in SQLite console. |
Sqlite admitted this as a bug and fixed in https://sqlite.org/src/info/a818ba2ed635b91e |
Hello, I did some research about FOREIGN KEY constraint, and It turns out that it's not enforced in following situations:
The record is not inserted in database, but NO error is produced.
I tracked this bug after noticing strange behavior in GORM.
Here I provide a reproduction code with behavior diagnosis.
I also conduct the same queries in Postgres, to show what's believed to be a correct behavior.
The diagnose output that I get:
The code to reproduce:
The docker-compose.yml to quickly spin up the postgres:
The text was updated successfully, but these errors were encountered: