Closed ugexe closed 1 month ago
I used git bisect and found that the issue was introduced in 5747f37d9cd5ba79cfe6209f79b0c1b074f11fe6.
I've also found that json(b) has nothing to do with it. SELECT 'foobar' = $1
has the same issue. It is something to do with encoding into text.
Still investigating.
I'm also able to reproduce it with pgx directly without going through stdlib.
I've now been able to reproduce it without github.com/vgarvardt/pgx-google-uuid/v5
. I think I'm close to understanding the issue.
New repro code:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/jackc/pgx/v5"
)
func main() {
conn, err := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL"))
defer conn.Close(context.Background())
uuid := [16]byte{}
var exists bool
err = conn.QueryRow(
context.Background(),
`SELECT 'foobar' = $1`,
uuid,
).Scan(&exists)
if err != nil {
log.Fatal(err)
}
fmt.Println("Exists:", exists)
}
Okay, I think I understand what is going on now.
pgx tries really hard to "just work" with whatever types you use. Part of how that works is when it doesn't know how to encode a particular Go type into a particular PostgreSQL type it tries various strategies to convert the Go value into a type that it does know how to handle. For example, it will dereference pointers and it convert values into their underlying types. For example, for type T int64
pgx doesn't know how to encode a T
into PostgreSQL, but it figures out that it can be converted into an int64
, and it does know how to encode that.
This process is attempted recursively. Here lies the problem. Your query is not actually attempting to send auuid
to PostgreSQL. It is sending a text
. pgx was getting caught on a path where it tried to wrap the uuid in something that maybe it could encode as text. But when it couldn't, it kept trying down that path, and unwrapped the value into a base Go type. And it continued recursively wrapping and unwrapping the value until it encountered a stack overflow.
I've implemented a depth check to the planning systems for encoding and scanning where they will give up when they reach a certain level. See 123b59a57e453d73779eeb5a74ec25195da7d34b.
This should resolve the issue.
This also occurs when doing something like SELECT COALESCE($2, $1)
where both $1 and $2 are actual uuids,
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/gofrs/uuid/v5"
pgxuuid "github.com/jackc/pgx-gofrs-uuid"
"github.com/jackc/pgx/v5"
)
func main() {
ctx := context.Background()
conn, err := pgx.Connect(ctx, os.Getenv("DATABASE_URL"))
if err != nil {
log.Fatal(err)
}
defer conn.Close(ctx)
pgxuuid.Register(conn.TypeMap())
uuid1 := uuid.Must(uuid.NewV4())
var uuid2 *uuid.UUID
var r *uuid.UUID
if err := conn.QueryRow(
ctx,
`SELECT COALESCE($2, $1)`,
uuid1,
uuid2,
).Scan(&r); err != nil {
log.Fatal(err)
}
fmt.Println("ret:", r)
}
Edit: my actual code did something like INSERT INTO table (id) VALUES(COALESCE($1, $2))
which led me here when upgrading from v4 to v5
Describe the bug After upgrading from 5.6.0 to 5.7.1 the following program results in a stackoverflow. It seems somewhat related to https://github.com/jackc/pgx/issues/1331 (stackoverflow that requires
pgx-google-uuid
) and https://github.com/jackc/pgx/issues/2125 (custom type and jsonb, although they state that issue is in 5.6.0 whereas my issue did not occur in 5.6.0).To Reproduce
Expected behavior
Actual behavior
Version
go version go1.23.1 linux/amd64
PostgreSQL 16.4 (Ubuntu 16.4-1.pgdg20.04+2) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0, 64-bit
v5.7.1