You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm not sure if this is a bug or more just a consequence of how parameter handling is implemented in the pgx library, especially when using custom types. But i've just noticed that using QueryExecModeCacheStatement vs QueryExecModeSimpleProtocol when building and executing a psql query results in two different sql queries, (and in my case one of which is wrong and causes an error):
To Reproduce
package main
import (
"context""log""github.com/jackc/pgx/v5"
)
typeSomeFancyNumberintconst (
FancyNumber1SomeFancyNumber=iotaFancyNumber2
)
func (sSomeFancyNumber) String() string {
switchs {
caseFancyNumber1:
return"fancy_number_1"caseFancyNumber2:
return"fancy_number_2"
}
return"unknown"
}
funcmain() {
simpleConn, err:=pgx.Connect(context.Background(), "host=postgres port=5432 user=auser password=apassword dbname=adbname sslmode=disable default_query_exec_mode=simple_protocol")
iferr!=nil {
log.Fatal(err)
}
defersimpleConn.Close(context.Background())
cacheStatementConn, err:=pgx.Connect(context.Background(), "host=postgres port=5432 user=auser password=apassword dbname=adbname sslmode=disable default_query_exec_mode=cache_statement")
iferr!=nil {
log.Fatal(err)
}
defercacheStatementConn.Close(context.Background())
// Create a table and put one record in it//_, err=simpleConn.Exec(context.Background(), `CREATE TABLE IF NOT EXISTS test (id SERIAL PRIMARY KEY, anumber INT NOT NULL);`)
iferr!=nil {
log.Fatalf("Failed to create table: %v", err)
}
log.Println("Table 'test' created successfully!")
_, err=simpleConn.Exec(context.Background(), `INSERT INTO test (anumber) VALUES ($1);`, FancyNumber1) // Replace 42 with the desired valueiferr!=nil {
log.Printf("Simple protocol exec resulted in error %v", err)
} else {
log.Println("Record inserted successfully into 'test' table using simple protocol mode!")
}
_, err=cacheStatementConn.Exec(context.Background(), `INSERT INTO test (anumber) VALUES ($1);`, FancyNumber1) // Replace 42 with the desired valueiferr!=nil {
log.Printf("Cache statement exec resulted in error %v", err)
} else {
log.Println("Record inserted successfully into 'test' table using cache statement mode!")
}
}
Expected behavior
identical or equivalent resolved queries irregardless of the execution modes of simple_protocol and cache_statement
i.e a log entry of both
Record inserted successfully into 'test' table using simple protocol mode!
Record inserted successfully into 'test' table using cache statement mode!
Actual behavior
The queries are resolved differently for the different execution modes.
For default_query_exec_mode=simple_protocol; the query resolves to:
INSERT INTO test (anumber) VALUES ("fancy_number_1");
For default_query_exec_mode=cache_statement; the query resolves to:
INSERT INTO test (anumber) VALUES (0);
Version
Go: go1.22 darwin/amd64
PostgreSQL: container image: postgres:16-alpine
pgx: v5.7.1
Additional context
This can be worked around by explicitly converting SomeFancyNumber to an int before passing it to the Exec function. While this may not a bug in the strictest sense, it does represent a design quirk in the library that may be helpful to document in the pgx library usage guidelines. Users switching modes between cache_statement and simple_protocol might not expect that some of their queries need to be tweaked/refactor.
The text was updated successfully, but these errors were encountered:
This behavior is undesirable, but I believe it is ultimately unavoidable unless you can give pgx some help.
When using QueryExecModeCacheStatement, pgx knows that the desired type for $1 is a PostgreSQL int4. It tries to find a path to a binary encoded PostgreSQL int4 and finds it by converting SomeFancyNumber to its underlying Go int type.
When using QueryExecModeSimpleProtocol pgx can only use the text protocol and it doesn't know what the PostgreSQL type is. You can let pgx know the desired type with:
Unfortunately, that is not quite enough. pgx tries to convert SomeFancyNumber to directly to a PostgreSQL int4 first, but it can't. As mentioned above, the simple protocol must use the text protocol. So pgx tries to convert SomeFancyNumber to a string which it can because SomeFancyNumber implements fmt.Stringer. pgx finds that path to encode the value before it finds the path of converting SomeFancyNumber to its underlying Go int type.
What does solve the problem is also implementing pgtype.Int64Valuer. That path is found before the fmt.Stringer path.
I'm not sure if this is a bug or more just a consequence of how parameter handling is implemented in the pgx library, especially when using custom types. But i've just noticed that using
QueryExecModeCacheStatement
vsQueryExecModeSimpleProtocol
when building and executing a psql query results in two different sql queries, (and in my case one of which is wrong and causes an error):To Reproduce
Expected behavior
identical or equivalent resolved queries irregardless of the execution modes of
simple_protocol
andcache_statement
i.e a log entry of both
Actual behavior
The queries are resolved differently for the different execution modes.
For
default_query_exec_mode=simple_protocol
; the query resolves to:For
default_query_exec_mode=cache_statement
; the query resolves to:Version
go1.22 darwin/amd64
container image: postgres:16-alpine
v5.7.1
Additional context
This can be worked around by explicitly converting
SomeFancyNumber
to anint
before passing it to the Exec function. While this may not a bug in the strictest sense, it does represent a design quirk in the library that may be helpful to document in the pgx library usage guidelines. Users switching modes betweencache_statement
andsimple_protocol
might not expect that some of their queries need to be tweaked/refactor.The text was updated successfully, but these errors were encountered: