Назван подготовленный оператор в pgx lib, как он работает?
Вступление
базы данных / SQL
В стандартной библиотеке SQL Go *Stmt
Тип имеет методы, определенные как:
func (s *Stmt) Exec(args ...interface{}) (Result, error)
func (s *Stmt) Query(args ...interface{}) (*Rows, error)
Новый (неназванный) оператор подготовлен:
func (db *DB) Prepare(query string) (*Stmt, error)
- Пул подключений абстрагирован и не доступен напрямую
- Сделка подготовлена на одном подключении
- Если соединение не доступно во время выполнения устава, оно будет повторно подготовлено для нового соединения.
PGX
PreparedStatement
Тип не имеет методов, определенных. Новое именованное подготовленное заявление подготовлено:
func (p *ConnPool) Prepare(name, sql string) (*PreparedStatement, error)
- Операции находятся непосредственно в пуле соединений
- Транзакция готовится на всех соединениях пула
- Не существует четкого способа выполнения подготовленного оператора
В комментарии к Github автор лучше объясняет различия архитектуры между pgx и database / sql. Документация по Prepare
Также говорится (выделено мое):
Приготовьтесь - идемпотент; то есть безопасно вызывать Prepare несколько раз с одним и тем же именем и аргументами sql. Это позволяет использовать код пути Prepare и Query / Exec / PrepareEx, не беспокоясь о том, подготовлен ли оператор.
Маленький пример
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?
}
Вопросы)
-
name
аргумент создает у меня впечатление, что его можно выполнить, вызвав егоname
, но как? - Документация создает впечатление, что
Query
/Exec
методы каким-то образом используют подготовленные заявления. Однако эти методы не требуютname
аргумент. Как это соответствует им? - Предположительно, сопоставление выполняется по содержанию запроса. Тогда в чем смысл именования утверждений?
Возможные ответы
Вот как далеко я попал:
- Нет методов, которые ссылаются на запросы по имени (предположение)
- Соответствие выполняется в теле запроса в
conn.ExecEx()
, Если это еще не подготовлено, это будет сделано:
ps, ok := c.preparedStatements[sql]
if !ok {
var err error
ps, err = c.prepareEx("", sql, nil)
if err != nil {
return "", err
}
}
- Самому PosgreSQL это нужно для чего-то (предположение).
1 ответ
@mkopriva отметила, что sql
текст вводил меня в заблуждение. Здесь есть двойная функция. Если sql
переменная не соответствует ключу в c.preparedStatements[sql]
карта, запрос, содержащийся в sql
готовится и новый *PreparedStatement
структура назначена ps
, Если он соответствует ключу, ps
переменная будет указывать на запись карты.
Таким образом, вы можете сделать что-то вроде:
package main
import (
"fmt"
"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)
}
if _, err := db.Prepare("my-query", "select $1::int"); err != nil {
panic(err)
}
row := db.QueryRow("my-query", 10)
var i int
if err := row.Scan(&i); err != nil {
panic(err)
}
fmt.Println(i)
}