From bd76a968826bd062a39c0cd5d04184abe0d1b231 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Sat, 16 Dec 2017 12:46:27 -0600 Subject: [PATCH] Add ConnConfig.PreferSimpleProtocol Allows configuring on a connection basis to prefer the simple protocol / disable implicit prepared statements. refs #331 --- conn.go | 13 ++++++++++++- conn_test.go | 25 +++++++++++++++++++++++++ query.go | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/conn.go b/conn.go index a1542b1c..bcbf88ad 100644 --- a/conn.go +++ b/conn.go @@ -75,6 +75,17 @@ type ConnConfig struct { RuntimeParams map[string]string // Run-time parameters to set on connection as session default values (e.g. search_path or application_name) OnNotice NoticeHandler // Callback function called when a notice response is received. CustomConnInfo func(*Conn) (*pgtype.ConnInfo, error) // Callback function to implement connection strategies for different backends. crate, pgbouncer, pgpool, etc. + + // PreferSimpleProtocol disables implicit prepared statement usage. By default + // pgx automatically uses the unnamed prepared statement for Query and + // QueryRow. It also uses a prepared statement when Exec has arguments. This + // can improve performance due to being able to use the binary format. It also + // does not rely on client side parameter sanitization. However, it does incur + // two round-trips per query and may be incompatible proxies such as + // PGBouncer. Setting PreferSimpleProtocol causes the simple protocol to be + // used by default. The same functionality can be controlled on a per query + // basis by setting QueryExOptions.SimpleProtocol. + PreferSimpleProtocol bool } func (cc *ConnConfig) networkAddress() (network, address string) { @@ -1586,7 +1597,7 @@ func (c *Conn) execEx(ctx context.Context, sql string, options *QueryExOptions, err = c.termContext(err) }() - if options != nil && options.SimpleProtocol { + if (options == nil && c.config.PreferSimpleProtocol) || (options != nil && options.SimpleProtocol) { err = c.sanitizeAndSendSimpleQuery(sql, arguments...) if err != nil { return "", err diff --git a/conn_test.go b/conn_test.go index d8781158..c1cb4ebe 100644 --- a/conn_test.go +++ b/conn_test.go @@ -255,6 +255,31 @@ func TestConnectWithConnectionRefused(t *testing.T) { } } +func TestConnectWithPreferSimpleProtocol(t *testing.T) { + t.Parallel() + + connConfig := *defaultConnConfig + connConfig.PreferSimpleProtocol = true + + conn := mustConnect(t, connConfig) + defer closeConn(t, conn) + + // If simple protocol is used we should be able to correctly scan the result + // into a pgtype.Text as the integer will have been encoded in text. + + var s pgtype.Text + err := conn.QueryRow("select $1::int4", 42).Scan(&s) + if err != nil { + t.Fatal(err) + } + + if s.Get() != "42" { + t.Fatalf(`expected "42", got %v`, s) + } + + ensureConnValid(t, conn) +} + func TestConnectCustomDialer(t *testing.T) { t.Parallel() diff --git a/query.go b/query.go index b90d43b2..3576091f 100644 --- a/query.go +++ b/query.go @@ -393,7 +393,7 @@ func (c *Conn) QueryEx(ctx context.Context, sql string, options *QueryExOptions, return rows, rows.err } - if options != nil && options.SimpleProtocol { + if (options == nil && c.config.PreferSimpleProtocol) || (options != nil && options.SimpleProtocol) { err = c.sanitizeAndSendSimpleQuery(sql, args...) if err != nil { rows.fatal(err)