2
0

Merge pull request #1 from jackc/master

Sync with upstream
This commit is contained in:
Artemiy Ryabinkov
2019-07-14 18:21:18 +03:00
committed by GitHub
13 changed files with 90 additions and 10 deletions
+18
View File
@@ -1,3 +1,21 @@
# 3.5.0 (June 29, 2019)
## Features
* Protocol support for PortalSuspended message (avivklas)
* Read OIDs for composite types on connection init (Nick Jones)
## Fixes
* Hstore can have empty keys (Josh Leverette)
* Fix -0 value for numeric type (David Hudson)
* Log error message on rows-close error (Euan Kemp)
## Changes
* Explicitly cast binary string to bytea in simple protocol (jinhua luo)
* Skip parse and sanitize simple query when no arguments (jinhua luo)
# 3.4.0 (May 3, 2019)
## Features
+4
View File
@@ -15,6 +15,10 @@ if err != nil {
}
```
## v4 Coming Soon
This is the current stable v3 version. v4 is currently is in prelease status. Consider using [v4](https://github.com/jackc/pgx/tree/v4) for new development or test upgrading existing applications.
## Features
pgx supports many additional features beyond what is available through database/sql.
+3 -1
View File
@@ -404,9 +404,11 @@ func initPostgresql(c *Conn) (*pgtype.ConnInfo, error) {
from pg_type t
left join pg_type base_type on t.typelem=base_type.oid
left join pg_namespace nsp on t.typnamespace=nsp.oid
left join pg_class cls on t.typrelid=cls.oid
where (
t.typtype in('b', 'p', 'r', 'e')
t.typtype in('b', 'p', 'r', 'e', 'c')
and (base_type.oid is null or base_type.typtype in('b', 'p', 'r'))
and (cls.oid is null or cls.relkind='c')
)`
)
+1 -1
View File
@@ -87,7 +87,7 @@ func QuoteString(str string) string {
}
func QuoteBytes(buf []byte) string {
return `'\x` + hex.EncodeToString(buf) + "'"
return `'\x` + hex.EncodeToString(buf) + "'::bytea"
}
type sqlLexer struct {
+1 -1
View File
@@ -108,7 +108,7 @@ func TestQuerySanitize(t *testing.T) {
{
query: sanitize.Query{Parts: []sanitize.Part{"select ", 1}},
args: []interface{}{[]byte{0, 1, 2, 3, 255}},
expected: `select '\x00010203ff'`,
expected: `select '\x00010203ff'::bytea`,
},
{
query: sanitize.Query{Parts: []sanitize.Part{"select ", 1}},
+1 -1
View File
@@ -19,7 +19,7 @@ func (pl *Logger) Log(level pgx.LogLevel, msg string, data map[string]interface{
fields := make([]zapcore.Field, len(data))
i := 0
for k, v := range data {
fields[i] = zap.Reflect(k, v)
fields[i] = zap.Any(k, v)
i++
}
+3 -1
View File
@@ -211,9 +211,11 @@ func PgxInitSteps() []Step {
from pg_type t
left join pg_type base_type on t.typelem=base_type.oid
left join pg_namespace nsp on t.typnamespace=nsp.oid
left join pg_class cls on t.typrelid=cls.oid
where (
t.typtype in('b', 'p', 'r', 'e')
t.typtype in('b', 'p', 'r', 'e', 'c')
and (base_type.oid is null or base_type.typtype in('b', 'p', 'r'))
and (cls.oid is null or cls.relkind='c')
)`,
}),
ExpectMessage(&pgproto3.Describe{
+6 -3
View File
@@ -23,7 +23,7 @@ type Frontend struct {
copyInResponse CopyInResponse
copyOutResponse CopyOutResponse
copyDone CopyDone
copyFail CopyFail
copyFail CopyFail
dataRow DataRow
emptyQueryResponse EmptyQueryResponse
errorResponse ErrorResponse
@@ -36,6 +36,7 @@ type Frontend struct {
parseComplete ParseComplete
readyForQuery ReadyForQuery
rowDescription RowDescription
portalSuspended PortalSuspended
bodyLen int
msgType byte
@@ -76,8 +77,8 @@ func (b *Frontend) Receive() (BackendMessage, error) {
msg = &b.notificationResponse
case 'c':
msg = &b.copyDone
case 'f':
msg = &b.copyFail
case 'f':
msg = &b.copyFail
case 'C':
msg = &b.commandComplete
case 'd':
@@ -112,6 +113,8 @@ func (b *Frontend) Receive() (BackendMessage, error) {
msg = &b.copyBothResponse
case 'Z':
msg = &b.readyForQuery
case 's':
msg = &b.portalSuspended
default:
return nil, errors.Errorf("unknown message type: %c", b.msgType)
}
+29
View File
@@ -0,0 +1,29 @@
package pgproto3
import (
"encoding/json"
)
type PortalSuspended struct{}
func (*PortalSuspended) Backend() {}
func (dst *PortalSuspended) Decode(src []byte) error {
if len(src) != 0 {
return &invalidMessageLenErr{messageType: "PortalSuspended", expectedLen: 0, actualLen: len(src)}
}
return nil
}
func (src *PortalSuspended) Encode(dst []byte) []byte {
return append(dst, 's', 0, 0, 0, 4)
}
func (src *PortalSuspended) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type string
}{
Type: "PortalSuspended",
})
}
+1 -1
View File
@@ -321,7 +321,7 @@ func parseNumericString(str string) (n *big.Int, exp int32, err error) {
if len(parts) > 1 {
exp = int32(-len(parts[1]))
} else {
for len(digits) > 1 && digits[len(digits)-1] == '0' {
for len(digits) > 1 && digits[len(digits)-1] == '0' && digits[len(digits)-2] != '-' {
digits = digits[:len(digits)-1]
exp++
}
+15
View File
@@ -1,6 +1,7 @@
package pgtype_test
import (
"math"
"math/big"
"reflect"
"testing"
@@ -65,6 +66,13 @@ func TestNumericArraySet(t *testing.T) {
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
Status: pgtype.Present},
},
{
source: []float32{float32(math.Copysign(0, -1))},
result: pgtype.NumericArray{
Elements: []pgtype.Numeric{{Int: big.NewInt(0), Status: pgtype.Present}},
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
Status: pgtype.Present},
},
{
source: []float64{1},
result: pgtype.NumericArray{
@@ -72,6 +80,13 @@ func TestNumericArraySet(t *testing.T) {
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
Status: pgtype.Present},
},
{
source: []float64{math.Copysign(0, -1)},
result: pgtype.NumericArray{
Elements: []pgtype.Numeric{{Int: big.NewInt(0), Status: pgtype.Present}},
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
Status: pgtype.Present},
},
{
source: (([]float32)(nil)),
result: pgtype.NumericArray{Status: pgtype.Null},
+3
View File
@@ -1,6 +1,7 @@
package pgtype_test
import (
"math"
"math/big"
"math/rand"
"reflect"
@@ -188,7 +189,9 @@ func TestNumericSet(t *testing.T) {
result *pgtype.Numeric
}{
{source: float32(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}},
{source: float32(math.Copysign(0, -1)), result: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Present}},
{source: float64(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}},
{source: float64(math.Copysign(0, -1)), result: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Present}},
{source: int8(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}},
{source: int16(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}},
{source: int32(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}},
+5 -1
View File
@@ -84,7 +84,7 @@ func (rows *Rows) Close() {
rows.conn.log(LogLevelInfo, "Query", map[string]interface{}{"sql": rows.sql, "args": logQueryArgs(rows.args), "time": endTime.Sub(rows.startTime), "rowCount": rows.rowCount})
}
} else if rows.conn.shouldLog(LogLevelError) {
rows.conn.log(LogLevelError, "Query", map[string]interface{}{"sql": rows.sql, "args": logQueryArgs(rows.args)})
rows.conn.log(LogLevelError, "Query", map[string]interface{}{"sql": rows.sql, "args": logQueryArgs(rows.args), "err": rows.err})
}
if rows.batch != nil && rows.err != nil {
@@ -522,6 +522,10 @@ func (c *Conn) readUntilRowDescription() ([]FieldDescription, error) {
}
func (c *Conn) sanitizeAndSendSimpleQuery(sql string, args ...interface{}) (err error) {
if len(args) == 0 {
return c.sendSimpleQuery(sql)
}
if c.RuntimeParams["standard_conforming_strings"] != "on" {
return errors.New("simple protocol queries must be run with standard_conforming_strings=on")
}