Inconsistent Parameter Handling Between QueryExecModeCacheStatement and QueryExecModeSimpleProtocol
- Dominant language
- Go
- Stars
- 14.3k
- Forks
- 1.1k
- Avg merge
- 6d 9h
- Merged PRs (30d)
- 11
Description
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**
```go
package main
import (
"context"
"log"
"github.com/jackc/pgx/v5"
)
type SomeFancyNumber int
const (
FancyNumber1 SomeFancyNumber = iota
FancyNumber2
)
func (s SomeFancyNumber) String() string {
switch s {
case FancyNumber1:
return "fancy_number_1"
case FancyNumber2:
return "fancy_number_2"
}
return "unknown"
}
func main() {
simpleConn, err := pgx.Connect(context.Background(), "host=postgres port=5432 user=auser password=apassword dbname=adbname sslmode=disable default_query_exec_mode=simple_protocol")
if err != nil {
log.Fatal(err)
}
defer simpleConn.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")
if err != nil {
log.Fatal(err)
}
defer cacheStatementConn.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);`)
if err != 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 value
if err != 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 value
if err != 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:
```sql
INSERT INTO test (anumber) VALUES ("fancy_number_1");
```
For `default_query_exec_mode=cache_statement`; the query resolves to:
```sql
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.
Contributor guide
Research direction
Start by running the provided Go reproduction against PostgreSQL with default_query_exec_mode set to simple_protocol and cache_statement, then compare how SomeFancyNumber is encoded. Trace parameter handling for both execution modes and check whether the differing results are intentional. Done means the behavior is aligned or the mode-specific limitation is documented with a clear workaround.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, postgresql
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100