mirror of
https://github.com/jackc/pgx.git
synced 2025-05-03 06:00:18 +00:00
This is in preparation for a Begin / Tx interface that will similate nested transactions with savepoints. In addition, this passes the TxOptions struct by value and thereby removes an allocation.
718 lines
16 KiB
Go
718 lines
16 KiB
Go
package pgx_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/jackc/pgtype"
|
|
"github.com/jackc/pgx/v4"
|
|
)
|
|
|
|
func BenchmarkMinimalPreparedSelect(b *testing.B) {
|
|
conn := mustConnect(b, mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE")))
|
|
defer closeConn(b, conn)
|
|
|
|
_, err := conn.Prepare(context.Background(), "ps1", "select $1::int8")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
var n int64
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
err = conn.QueryRow(context.Background(), "ps1", i).Scan(&n)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
if n != int64(i) {
|
|
b.Fatalf("expected %d, got %d", i, n)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkMinimalPgConnPreparedSelect(b *testing.B) {
|
|
conn := mustConnect(b, mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE")))
|
|
defer closeConn(b, conn)
|
|
|
|
pgConn := conn.PgConn()
|
|
|
|
_, err := pgConn.Prepare(context.Background(), "ps1", "select $1::int8", nil)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
encodedBytes := make([]byte, 8)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
rr := pgConn.ExecPrepared(context.Background(), "ps1", [][]byte{encodedBytes}, []int16{1}, []int16{1})
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
for rr.NextRow() {
|
|
for i := range rr.Values() {
|
|
if bytes.Compare(rr.Values()[0], encodedBytes) != 0 {
|
|
b.Fatalf("unexpected values: %s %s", rr.Values()[i], encodedBytes)
|
|
}
|
|
}
|
|
}
|
|
_, err = rr.Close()
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkPointerPointerWithNullValues(b *testing.B) {
|
|
conn := mustConnect(b, mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE")))
|
|
defer closeConn(b, conn)
|
|
|
|
_, err := conn.Prepare(context.Background(), "selectNulls", "select 1::int4, 'johnsmith', null::text, null::text, null::text, null::date, null::timestamptz")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
var record struct {
|
|
id int32
|
|
userName string
|
|
email *string
|
|
name *string
|
|
sex *string
|
|
birthDate *time.Time
|
|
lastLoginTime *time.Time
|
|
}
|
|
|
|
err = conn.QueryRow(context.Background(), "selectNulls").Scan(
|
|
&record.id,
|
|
&record.userName,
|
|
&record.email,
|
|
&record.name,
|
|
&record.sex,
|
|
&record.birthDate,
|
|
&record.lastLoginTime,
|
|
)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
// These checks both ensure that the correct data was returned
|
|
// and provide a benchmark of accessing the returned values.
|
|
if record.id != 1 {
|
|
b.Fatalf("bad value for id: %v", record.id)
|
|
}
|
|
if record.userName != "johnsmith" {
|
|
b.Fatalf("bad value for userName: %v", record.userName)
|
|
}
|
|
if record.email != nil {
|
|
b.Fatalf("bad value for email: %v", record.email)
|
|
}
|
|
if record.name != nil {
|
|
b.Fatalf("bad value for name: %v", record.name)
|
|
}
|
|
if record.sex != nil {
|
|
b.Fatalf("bad value for sex: %v", record.sex)
|
|
}
|
|
if record.birthDate != nil {
|
|
b.Fatalf("bad value for birthDate: %v", record.birthDate)
|
|
}
|
|
if record.lastLoginTime != nil {
|
|
b.Fatalf("bad value for lastLoginTime: %v", record.lastLoginTime)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkPointerPointerWithPresentValues(b *testing.B) {
|
|
conn := mustConnect(b, mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE")))
|
|
defer closeConn(b, conn)
|
|
|
|
_, err := conn.Prepare(context.Background(), "selectNulls", "select 1::int4, 'johnsmith', 'johnsmith@example.com', 'John Smith', 'male', '1970-01-01'::date, '2015-01-01 00:00:00'::timestamptz")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
var record struct {
|
|
id int32
|
|
userName string
|
|
email *string
|
|
name *string
|
|
sex *string
|
|
birthDate *time.Time
|
|
lastLoginTime *time.Time
|
|
}
|
|
|
|
err = conn.QueryRow(context.Background(), "selectNulls").Scan(
|
|
&record.id,
|
|
&record.userName,
|
|
&record.email,
|
|
&record.name,
|
|
&record.sex,
|
|
&record.birthDate,
|
|
&record.lastLoginTime,
|
|
)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
// These checks both ensure that the correct data was returned
|
|
// and provide a benchmark of accessing the returned values.
|
|
if record.id != 1 {
|
|
b.Fatalf("bad value for id: %v", record.id)
|
|
}
|
|
if record.userName != "johnsmith" {
|
|
b.Fatalf("bad value for userName: %v", record.userName)
|
|
}
|
|
if record.email == nil || *record.email != "johnsmith@example.com" {
|
|
b.Fatalf("bad value for email: %v", record.email)
|
|
}
|
|
if record.name == nil || *record.name != "John Smith" {
|
|
b.Fatalf("bad value for name: %v", record.name)
|
|
}
|
|
if record.sex == nil || *record.sex != "male" {
|
|
b.Fatalf("bad value for sex: %v", record.sex)
|
|
}
|
|
if record.birthDate == nil || *record.birthDate != time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local) {
|
|
b.Fatalf("bad value for birthDate: %v", record.birthDate)
|
|
}
|
|
if record.lastLoginTime == nil || *record.lastLoginTime != time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local) {
|
|
b.Fatalf("bad value for lastLoginTime: %v", record.lastLoginTime)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSelectWithoutLogging(b *testing.B) {
|
|
conn := mustConnect(b, mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE")))
|
|
defer closeConn(b, conn)
|
|
|
|
benchmarkSelectWithLog(b, conn)
|
|
}
|
|
|
|
type discardLogger struct{}
|
|
|
|
func (dl discardLogger) Log(ctx context.Context, level pgx.LogLevel, msg string, data map[string]interface{}) {
|
|
}
|
|
|
|
func BenchmarkSelectWithLoggingTraceDiscard(b *testing.B) {
|
|
var logger discardLogger
|
|
config := mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE"))
|
|
config.Logger = logger
|
|
config.LogLevel = pgx.LogLevelTrace
|
|
|
|
conn := mustConnect(b, config)
|
|
defer closeConn(b, conn)
|
|
|
|
benchmarkSelectWithLog(b, conn)
|
|
}
|
|
|
|
func BenchmarkSelectWithLoggingDebugWithDiscard(b *testing.B) {
|
|
var logger discardLogger
|
|
config := mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE"))
|
|
config.Logger = logger
|
|
config.LogLevel = pgx.LogLevelDebug
|
|
|
|
conn := mustConnect(b, config)
|
|
defer closeConn(b, conn)
|
|
|
|
benchmarkSelectWithLog(b, conn)
|
|
}
|
|
|
|
func BenchmarkSelectWithLoggingInfoWithDiscard(b *testing.B) {
|
|
var logger discardLogger
|
|
config := mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE"))
|
|
config.Logger = logger
|
|
config.LogLevel = pgx.LogLevelInfo
|
|
|
|
conn := mustConnect(b, config)
|
|
defer closeConn(b, conn)
|
|
|
|
benchmarkSelectWithLog(b, conn)
|
|
}
|
|
|
|
func BenchmarkSelectWithLoggingErrorWithDiscard(b *testing.B) {
|
|
var logger discardLogger
|
|
config := mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE"))
|
|
config.Logger = logger
|
|
config.LogLevel = pgx.LogLevelError
|
|
|
|
conn := mustConnect(b, config)
|
|
defer closeConn(b, conn)
|
|
|
|
benchmarkSelectWithLog(b, conn)
|
|
}
|
|
|
|
func benchmarkSelectWithLog(b *testing.B, conn *pgx.Conn) {
|
|
_, err := conn.Prepare(context.Background(), "test", "select 1::int4, 'johnsmith', 'johnsmith@example.com', 'John Smith', 'male', '1970-01-01'::date, '2015-01-01 00:00:00'::timestamptz")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
var record struct {
|
|
id int32
|
|
userName string
|
|
email string
|
|
name string
|
|
sex string
|
|
birthDate time.Time
|
|
lastLoginTime time.Time
|
|
}
|
|
|
|
err = conn.QueryRow(context.Background(), "test").Scan(
|
|
&record.id,
|
|
&record.userName,
|
|
&record.email,
|
|
&record.name,
|
|
&record.sex,
|
|
&record.birthDate,
|
|
&record.lastLoginTime,
|
|
)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
// These checks both ensure that the correct data was returned
|
|
// and provide a benchmark of accessing the returned values.
|
|
if record.id != 1 {
|
|
b.Fatalf("bad value for id: %v", record.id)
|
|
}
|
|
if record.userName != "johnsmith" {
|
|
b.Fatalf("bad value for userName: %v", record.userName)
|
|
}
|
|
if record.email != "johnsmith@example.com" {
|
|
b.Fatalf("bad value for email: %v", record.email)
|
|
}
|
|
if record.name != "John Smith" {
|
|
b.Fatalf("bad value for name: %v", record.name)
|
|
}
|
|
if record.sex != "male" {
|
|
b.Fatalf("bad value for sex: %v", record.sex)
|
|
}
|
|
if record.birthDate != time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local) {
|
|
b.Fatalf("bad value for birthDate: %v", record.birthDate)
|
|
}
|
|
if record.lastLoginTime != time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local) {
|
|
b.Fatalf("bad value for lastLoginTime: %v", record.lastLoginTime)
|
|
}
|
|
}
|
|
}
|
|
|
|
const benchmarkWriteTableCreateSQL = `drop table if exists t;
|
|
|
|
create table t(
|
|
varchar_1 varchar not null,
|
|
varchar_2 varchar not null,
|
|
varchar_null_1 varchar,
|
|
date_1 date not null,
|
|
date_null_1 date,
|
|
int4_1 int4 not null,
|
|
int4_2 int4 not null,
|
|
int4_null_1 int4,
|
|
tstz_1 timestamptz not null,
|
|
tstz_2 timestamptz,
|
|
bool_1 bool not null,
|
|
bool_2 bool not null,
|
|
bool_3 bool not null
|
|
);
|
|
`
|
|
|
|
const benchmarkWriteTableInsertSQL = `insert into t(
|
|
varchar_1,
|
|
varchar_2,
|
|
varchar_null_1,
|
|
date_1,
|
|
date_null_1,
|
|
int4_1,
|
|
int4_2,
|
|
int4_null_1,
|
|
tstz_1,
|
|
tstz_2,
|
|
bool_1,
|
|
bool_2,
|
|
bool_3
|
|
) values (
|
|
$1::varchar,
|
|
$2::varchar,
|
|
$3::varchar,
|
|
$4::date,
|
|
$5::date,
|
|
$6::int4,
|
|
$7::int4,
|
|
$8::int4,
|
|
$9::timestamptz,
|
|
$10::timestamptz,
|
|
$11::bool,
|
|
$12::bool,
|
|
$13::bool
|
|
)`
|
|
|
|
type benchmarkWriteTableCopyFromSrc struct {
|
|
count int
|
|
idx int
|
|
row []interface{}
|
|
}
|
|
|
|
func (s *benchmarkWriteTableCopyFromSrc) Next() bool {
|
|
s.idx++
|
|
return s.idx < s.count
|
|
}
|
|
|
|
func (s *benchmarkWriteTableCopyFromSrc) Values() ([]interface{}, error) {
|
|
return s.row, nil
|
|
}
|
|
|
|
func (s *benchmarkWriteTableCopyFromSrc) Err() error {
|
|
return nil
|
|
}
|
|
|
|
func newBenchmarkWriteTableCopyFromSrc(count int) pgx.CopyFromSource {
|
|
return &benchmarkWriteTableCopyFromSrc{
|
|
count: count,
|
|
row: []interface{}{
|
|
"varchar_1",
|
|
"varchar_2",
|
|
pgtype.Text{},
|
|
time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local),
|
|
pgtype.Date{},
|
|
1,
|
|
2,
|
|
pgtype.Int4{},
|
|
time.Date(2001, 1, 1, 0, 0, 0, 0, time.Local),
|
|
time.Date(2002, 1, 1, 0, 0, 0, 0, time.Local),
|
|
true,
|
|
false,
|
|
true,
|
|
},
|
|
}
|
|
}
|
|
|
|
func benchmarkWriteNRowsViaInsert(b *testing.B, n int) {
|
|
conn := mustConnect(b, mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE")))
|
|
defer closeConn(b, conn)
|
|
|
|
mustExec(b, conn, benchmarkWriteTableCreateSQL)
|
|
_, err := conn.Prepare(context.Background(), "insert_t", benchmarkWriteTableInsertSQL)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
src := newBenchmarkWriteTableCopyFromSrc(n)
|
|
|
|
tx, err := conn.Begin(context.Background())
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
for src.Next() {
|
|
values, _ := src.Values()
|
|
if _, err = tx.Exec(context.Background(), "insert_t", values...); err != nil {
|
|
b.Fatalf("Exec unexpectedly failed with: %v", err)
|
|
}
|
|
}
|
|
|
|
err = tx.Commit(context.Background())
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// note this function is only used for benchmarks -- it doesn't escape tableName
|
|
// or columnNames
|
|
func multiInsert(conn *pgx.Conn, tableName string, columnNames []string, rowSrc pgx.CopyFromSource) (int, error) {
|
|
maxRowsPerInsert := 65535 / len(columnNames)
|
|
rowsThisInsert := 0
|
|
rowCount := 0
|
|
|
|
sqlBuf := &bytes.Buffer{}
|
|
args := make(pgx.QueryArgs, 0)
|
|
|
|
resetQuery := func() {
|
|
sqlBuf.Reset()
|
|
fmt.Fprintf(sqlBuf, "insert into %s(%s) values", tableName, strings.Join(columnNames, ", "))
|
|
|
|
args = args[0:0]
|
|
|
|
rowsThisInsert = 0
|
|
}
|
|
resetQuery()
|
|
|
|
tx, err := conn.Begin(context.Background())
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer tx.Rollback(context.Background())
|
|
|
|
for rowSrc.Next() {
|
|
if rowsThisInsert > 0 {
|
|
sqlBuf.WriteByte(',')
|
|
}
|
|
|
|
sqlBuf.WriteByte('(')
|
|
|
|
values, err := rowSrc.Values()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
for i, val := range values {
|
|
if i > 0 {
|
|
sqlBuf.WriteByte(',')
|
|
}
|
|
sqlBuf.WriteString(args.Append(val))
|
|
}
|
|
|
|
sqlBuf.WriteByte(')')
|
|
|
|
rowsThisInsert++
|
|
|
|
if rowsThisInsert == maxRowsPerInsert {
|
|
_, err := tx.Exec(context.Background(), sqlBuf.String(), args...)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
rowCount += rowsThisInsert
|
|
resetQuery()
|
|
}
|
|
}
|
|
|
|
if rowsThisInsert > 0 {
|
|
_, err := tx.Exec(context.Background(), sqlBuf.String(), args...)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
rowCount += rowsThisInsert
|
|
}
|
|
|
|
if err := tx.Commit(context.Background()); err != nil {
|
|
return 0, nil
|
|
}
|
|
|
|
return rowCount, nil
|
|
|
|
}
|
|
|
|
func benchmarkWriteNRowsViaMultiInsert(b *testing.B, n int) {
|
|
conn := mustConnect(b, mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE")))
|
|
defer closeConn(b, conn)
|
|
|
|
mustExec(b, conn, benchmarkWriteTableCreateSQL)
|
|
_, err := conn.Prepare(context.Background(), "insert_t", benchmarkWriteTableInsertSQL)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
src := newBenchmarkWriteTableCopyFromSrc(n)
|
|
|
|
_, err := multiInsert(conn, "t",
|
|
[]string{"varchar_1",
|
|
"varchar_2",
|
|
"varchar_null_1",
|
|
"date_1",
|
|
"date_null_1",
|
|
"int4_1",
|
|
"int4_2",
|
|
"int4_null_1",
|
|
"tstz_1",
|
|
"tstz_2",
|
|
"bool_1",
|
|
"bool_2",
|
|
"bool_3"},
|
|
src)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func benchmarkWriteNRowsViaCopy(b *testing.B, n int) {
|
|
conn := mustConnect(b, mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE")))
|
|
defer closeConn(b, conn)
|
|
|
|
mustExec(b, conn, benchmarkWriteTableCreateSQL)
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
src := newBenchmarkWriteTableCopyFromSrc(n)
|
|
|
|
_, err := conn.CopyFrom(context.Background(),
|
|
pgx.Identifier{"t"},
|
|
[]string{"varchar_1",
|
|
"varchar_2",
|
|
"varchar_null_1",
|
|
"date_1",
|
|
"date_null_1",
|
|
"int4_1",
|
|
"int4_2",
|
|
"int4_null_1",
|
|
"tstz_1",
|
|
"tstz_2",
|
|
"bool_1",
|
|
"bool_2",
|
|
"bool_3"},
|
|
src)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkWrite5RowsViaInsert(b *testing.B) {
|
|
benchmarkWriteNRowsViaInsert(b, 5)
|
|
}
|
|
|
|
func BenchmarkWrite5RowsViaMultiInsert(b *testing.B) {
|
|
benchmarkWriteNRowsViaMultiInsert(b, 5)
|
|
}
|
|
|
|
func BenchmarkWrite5RowsViaCopy(b *testing.B) {
|
|
benchmarkWriteNRowsViaCopy(b, 5)
|
|
}
|
|
|
|
func BenchmarkWrite10RowsViaInsert(b *testing.B) {
|
|
benchmarkWriteNRowsViaInsert(b, 10)
|
|
}
|
|
|
|
func BenchmarkWrite10RowsViaMultiInsert(b *testing.B) {
|
|
benchmarkWriteNRowsViaMultiInsert(b, 10)
|
|
}
|
|
|
|
func BenchmarkWrite10RowsViaCopy(b *testing.B) {
|
|
benchmarkWriteNRowsViaCopy(b, 10)
|
|
}
|
|
|
|
func BenchmarkWrite100RowsViaInsert(b *testing.B) {
|
|
benchmarkWriteNRowsViaInsert(b, 100)
|
|
}
|
|
|
|
func BenchmarkWrite100RowsViaMultiInsert(b *testing.B) {
|
|
benchmarkWriteNRowsViaMultiInsert(b, 100)
|
|
}
|
|
|
|
func BenchmarkWrite100RowsViaCopy(b *testing.B) {
|
|
benchmarkWriteNRowsViaCopy(b, 100)
|
|
}
|
|
|
|
func BenchmarkWrite1000RowsViaInsert(b *testing.B) {
|
|
benchmarkWriteNRowsViaInsert(b, 1000)
|
|
}
|
|
|
|
func BenchmarkWrite1000RowsViaMultiInsert(b *testing.B) {
|
|
benchmarkWriteNRowsViaMultiInsert(b, 1000)
|
|
}
|
|
|
|
func BenchmarkWrite1000RowsViaCopy(b *testing.B) {
|
|
benchmarkWriteNRowsViaCopy(b, 1000)
|
|
}
|
|
|
|
func BenchmarkWrite10000RowsViaInsert(b *testing.B) {
|
|
benchmarkWriteNRowsViaInsert(b, 10000)
|
|
}
|
|
|
|
func BenchmarkWrite10000RowsViaMultiInsert(b *testing.B) {
|
|
benchmarkWriteNRowsViaMultiInsert(b, 10000)
|
|
}
|
|
|
|
func BenchmarkWrite10000RowsViaCopy(b *testing.B) {
|
|
benchmarkWriteNRowsViaCopy(b, 10000)
|
|
}
|
|
|
|
func BenchmarkMultipleQueriesNonBatch(b *testing.B) {
|
|
conn := mustConnect(b, mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE")))
|
|
defer closeConn(b, conn)
|
|
|
|
queryCount := 3
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
for j := 0; j < queryCount; j++ {
|
|
rows, err := conn.Query(context.Background(), "select n from generate_series(0, 5) n")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
for k := 0; rows.Next(); k++ {
|
|
var n int
|
|
if err := rows.Scan(&n); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
if n != k {
|
|
b.Fatalf("n => %v, want %v", n, k)
|
|
}
|
|
}
|
|
|
|
if rows.Err() != nil {
|
|
b.Fatal(rows.Err())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkMultipleQueriesBatch(b *testing.B) {
|
|
conn := mustConnect(b, mustParseConfig(b, os.Getenv("PGX_TEST_DATABASE")))
|
|
defer closeConn(b, conn)
|
|
|
|
queryCount := 3
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
batch := &pgx.Batch{}
|
|
for j := 0; j < queryCount; j++ {
|
|
batch.Queue("select n from generate_series(0,5) n",
|
|
nil,
|
|
nil,
|
|
[]int16{pgx.BinaryFormatCode},
|
|
)
|
|
}
|
|
|
|
br := conn.SendBatch(context.Background(), batch)
|
|
|
|
for j := 0; j < queryCount; j++ {
|
|
rows, err := br.QueryResults()
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
for k := 0; rows.Next(); k++ {
|
|
var n int
|
|
if err := rows.Scan(&n); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
if n != k {
|
|
b.Fatalf("n => %v, want %v", n, k)
|
|
}
|
|
}
|
|
|
|
if rows.Err() != nil {
|
|
b.Fatal(rows.Err())
|
|
}
|
|
}
|
|
|
|
err := br.Close()
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|