Introduction
database/sql
In the Go standard sql library, the *Stmt
type has methods defined like:
func (s *Stmt) Exec(args ...interface{}) (Result, error)
func (s *Stmt) Query(args ...interface{}) (*Rows, error)
The a new (unnamed) statement is prepared by:
func (db *DB) Prepare(query string) (*Stmt, error)
- Connection pool is abstracted and not directly accessible
- A transaction is prepared on a single connection
- If the connection is not available at statment execution time, it will be re-prepared on a new connection.
pgx
The PreparedStatement
type doesn't have any methods defined. A new named prepared statement is prepared by:
func (p *ConnPool) Prepare(name, sql string) (*PreparedStatement, error)
- Operations are directly on the connection pool
- The transaction gets prepared on all connections of the pool
- There is no clear way how to execute the prepared statement
In a Github comment, the author explains better the differences of architecture between pgx and database/sql. The documentation on Prepare
also states (emphasis mine):
Prepare is idempotent; i.e. it is safe to call Prepare multiple times with the same name and sql arguments. This allows a code path to Prepare and Query/Exec/PrepareEx without concern for if the statement has already been prepared.
Small example
package main
import (
"github.com/jackc/pgx"
)
func main() {
conf := pgx.ConnPoolConfig{
ConnConfig: pgx.ConnConfig{
Host: "/run/postgresql",
User: "postgres",
Database: "test",
},
MaxConnections: 5,
}
db, err := pgx.NewConnPool(conf)
if err != nil {
panic(err)
}
_, err = db.Prepare("my-query", "select $1")
if err != nil {
panic(err)
}
// What to do with the prepared statement?
}
Question(s)
- The
name
argument gives me the impression it can be executed by calling it byname
, but how? - The documentation gives the impression that
Query
/Exec
methods somehow leverage the prepared statements. However, those methods don't take aname
argument. How does it match them? - Presumably, matching is done by the query content. Then what's the whole point of naming statements?
Possible answers
This is how far I got myself:
- There are no methods that refer to the queries by name (assumption)
- Matching is done on the query body in
conn.ExecEx()
. If it is not yet prepared, it will be done:
ps, ok := c.preparedStatements[sql]
if !ok {
var err error
ps, err = c.prepareEx("", sql, nil)
if err != nil {
return "", err
}
}
- PosgreSQL itself needs it for something (assumption).
sql
argument toQuery/QueryRow/Exec
. this and this confirm this and thesendPreparedQuery
method then prepends the name withEXECUTE
. – Interceptionsql
will hold the name of the prepared statement, that's when you get a match on the lines linked in 2. – Interception