GORM Playground Link
https://github.com/go-gorm/playground/pull/168
Description
There is an issue when loading a model with composite primary keys from a sqlite3 database where the .Take() after a .Preload() will result in a sqlite3 row value misued
error.
The following model (which is used in the playground link, too) can create the issue.
type (
// Root is the root table with an autoincrement id
// it references Branch by branch's composite primary key
Root struct {
ID uint64 `gorm:"primaryKey;autoIncrement"`
Branches []*Branch `gorm:"foreignKey:RootId;constraint:OnUpdate:RESTRICT,OnDelete:CASCADE;"`
}
// Branch is has a composite primary key derived from the ID of its Root and its own Name
// it references Leaf by leaf's composite primary key
Branch struct {
RootId uint64 `gorm:"primaryKey"`
Name string `gorm:"primaryKey;size:100"`
Leaves []*Leaf `gorm:"foreignKey:RootId,BranchName;constraint:OnUpdate:RESTRICT,OnDelete:CASCADE;"`
}
// Leaf is has a composite primary key derived from the ID of its Root, Name of its Branch and its own Name
// it does not reference anything
Leaf struct {
RootId uint64 `gorm:"primaryKey"`
BranchName string `gorm:"primaryKey;size:100"`
Name string `gorm:"primaryKey;size:100"`
}
)
Basically when having a model composed of one root (ID=1) with two branches (b1 and b2) and a leaf on each branch the query to load the leaves in a preload is created as this:
SELECT * FROM `leafs` WHERE (`leafs`.`root_id`,`leafs`.`branch_name`) IN ((1,"b1"),(1,"b2"))
From my understanding, the tuples in the IN clause cause the issue, as the used sqlite3 database does not seem to handle this. The question is if the query could be build like the following, because this works with the used sqlite3 database, too.
SELECT * FROM `leafs` WHERE (`leafs`.`root_id`,`leafs`.`branch_name`) IN (VALUES (1,"b1"),(1,"b2"));
or
SELECT * FROM `leafs` WHERE (`leafs`.`root_id`) IN (1,1) AND (`leafs`.`branch_name`) IN ("b1","b2");
I verified however, that the above query will work for a MariaDB, so I guess this issue is related to sqlite3 only.
Comment From: fwielstra
This was 'just' a warning, but I recently updated my dependencies (gorm.io/driver/sqlite
from v1.1.3 to v1.1.4 and gorm.io/gorm
from v1.20.6 to v1.20.9) and it has caused one of my tests to fail because instead of a warning it now returns an error.
I narrowed it down to gorm v1.20.8; it remains a warning at v1.20.7, so it must have changed somewhere in these commits: https://github.com/go-gorm/gorm/compare/v1.20.7...v1.20.8
Comment From: jinzhu
It should not work before @fwielstra
Comment From: RobRoyd
How is the progress on this issue? Will it be fixed or is it a no fix?
Comment From: codemicro
I'm also seeing this with GORM versions 1.20.7
and 1.20.11
and the SQLite driver versions 1.1.4
and 1.1.6
, again with a composite key. This time it was as a result of a call to Delete
, though, not because of preloading.
// DELETE FROM `user_bios` WHERE (`user_bios`.`user_id`,`user_bios`.`mem_id`) IN (("[...]","[...]"))
type UserBio struct {
UserId string `gorm:"primarykey"`
MemId string `gorm:"primarykey"`
}
For anyone looking for a workaround, check out db.Exec.
Comment From: Z-M-Huang
I've encountered the same for SQLite. This actually breaks some of my test cases for structs having composite keys.
Comment From: foxcpp
Facing the same issue (using Preload)
The generated SQL in my case is:
SELECT * FROM `comments` WHERE `comments`.`group_id`,`comments`.`post_id` IN ((XXXX,YYYY));
The problematic part is ((XXXX,YYYY))
.
Quoting https://www.sqlite.org/rowvalue.html:
For a row-value IN operator, the left-hand side (hereafter "LHS") can be either a parenthesized list of values or a subquery with multiple columns. But the right-hand side (hereafter "RHS") must be a subquery expression.
Comment From: foxcpp
It seems like the only way to make it work for SQLite is to generate a SELECT subexpression using SELECT ... UNION
. This works correctly.
Comment From: RobRoyd
Can confirm. After updating gorm and the driver to latest, more tests are not running. It seems, it is because of tables containing composite keys. Before it was only when preloading, now it is on delete, create and update, too.
Comment From: JonathonReinhart
Just in case anyone ends up here for the same reason I did, it seems that the use of net.IP
fields with the sqlite driver does not work: https://github.com/go-gorm/sqlite/issues/36
Comment From: antoninbas
Just wanted to bump this issue as the problem still seems to exist with the latest Gorm version
Comment From: choyri
I just wanted to use SQLite for unittest in CICD, but unfortunately, I encountered this error, so I had to continue to use MySQL 😢
Comment From: ToughK
Any updates on this problem? I still get the same problem with the latest Gorm version
Comment From: LubosD
I've hit the same problem. Tried to find out if it could be easily remedied via the Dialector, but the code that builds this doesn't use it.
I think it really deserves to be fixed, because I think the "IN with multiple columns" example on GORM's homepage cannot work in SQLite this way.
Comment From: moshuipan
use github.com/glebarez/sqlite
to replace gorm.io/driver/sqlite
mabye resolve the problem.