mirror of https://github.com/jackc/pgx.git
Simplify encoding extended query arguments
parent
1cef9075d9
commit
e5685a34fc
|
@ -1,9 +1,7 @@
|
||||||
package pgx
|
package pgx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/jackc/pgx/v5/internal/anynil"
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -55,14 +53,7 @@ func (eqb *extendedQueryBuilder) Reset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (eqb *extendedQueryBuilder) encodeExtendedParamValue(m *pgtype.Map, oid uint32, formatCode int16, arg interface{}) ([]byte, error) {
|
func (eqb *extendedQueryBuilder) encodeExtendedParamValue(m *pgtype.Map, oid uint32, formatCode int16, arg interface{}) ([]byte, error) {
|
||||||
if arg == nil {
|
if anynil.Is(arg) {
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
refVal := reflect.ValueOf(arg)
|
|
||||||
argIsPtr := refVal.Kind() == reflect.Ptr
|
|
||||||
|
|
||||||
if argIsPtr && refVal.IsNil() {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,18 +63,6 @@ func (eqb *extendedQueryBuilder) encodeExtendedParamValue(m *pgtype.Map, oid uin
|
||||||
|
|
||||||
pos := len(eqb.paramValueBytes)
|
pos := len(eqb.paramValueBytes)
|
||||||
|
|
||||||
if arg, ok := arg.(string); ok {
|
|
||||||
return []byte(arg), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if argIsPtr {
|
|
||||||
// We have already checked that arg is not pointing to nil,
|
|
||||||
// so it is safe to dereference here.
|
|
||||||
arg = refVal.Elem().Interface()
|
|
||||||
return eqb.encodeExtendedParamValue(m, oid, formatCode, arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := m.TypeForOID(oid); ok {
|
|
||||||
buf, err := m.Encode(oid, formatCode, arg, eqb.paramValueBytes)
|
buf, err := m.Encode(oid, formatCode, arg, eqb.paramValueBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -95,12 +74,6 @@ func (eqb *extendedQueryBuilder) encodeExtendedParamValue(m *pgtype.Map, oid uin
|
||||||
return eqb.paramValueBytes[pos:], nil
|
return eqb.paramValueBytes[pos:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if strippedArg, ok := stripNamedType(&refVal); ok {
|
|
||||||
return eqb.encodeExtendedParamValue(m, oid, formatCode, strippedArg)
|
|
||||||
}
|
|
||||||
return nil, SerializationError(fmt.Sprintf("Cannot encode %T into oid %v - %T must implement Encoder or be converted to a string", arg, oid, arg))
|
|
||||||
}
|
|
||||||
|
|
||||||
// chooseParameterFormatCode determines the correct format code for an
|
// chooseParameterFormatCode determines the correct format code for an
|
||||||
// argument to a prepared statement. It defaults to TextFormatCode if no
|
// argument to a prepared statement. It defaults to TextFormatCode if no
|
||||||
// determination can be made.
|
// determination can be made.
|
||||||
|
|
|
@ -16,13 +16,37 @@ func (JSONCodec) PreferredFormat() int16 {
|
||||||
return TextFormatCode
|
return TextFormatCode
|
||||||
}
|
}
|
||||||
|
|
||||||
func (JSONCodec) PlanEncode(m *Map, oid uint32, format int16, value interface{}) EncodePlan {
|
func (c JSONCodec) PlanEncode(m *Map, oid uint32, format int16, value interface{}) EncodePlan {
|
||||||
switch value.(type) {
|
switch value.(type) {
|
||||||
|
case string:
|
||||||
|
return encodePlanJSONCodecEitherFormatString{}
|
||||||
case []byte:
|
case []byte:
|
||||||
return encodePlanJSONCodecEitherFormatByteSlice{}
|
return encodePlanJSONCodecEitherFormatByteSlice{}
|
||||||
default:
|
}
|
||||||
|
|
||||||
|
// Because anything can be marshalled the normal wrapping in Map.PlanScan doesn't get a chance to run. So try the
|
||||||
|
// appropriate wrappers here.
|
||||||
|
for _, f := range []TryWrapEncodePlanFunc{
|
||||||
|
TryWrapDerefPointerEncodePlan,
|
||||||
|
TryWrapFindUnderlyingTypeEncodePlan,
|
||||||
|
} {
|
||||||
|
if wrapperPlan, nextValue, ok := f(value); ok {
|
||||||
|
if nextPlan := c.PlanEncode(m, oid, format, nextValue); nextPlan != nil {
|
||||||
|
wrapperPlan.SetNext(nextPlan)
|
||||||
|
return wrapperPlan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return encodePlanJSONCodecEitherFormatMarshal{}
|
return encodePlanJSONCodecEitherFormatMarshal{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type encodePlanJSONCodecEitherFormatString struct{}
|
||||||
|
|
||||||
|
func (encodePlanJSONCodecEitherFormatString) Encode(value interface{}, buf []byte) (newBuf []byte, err error) {
|
||||||
|
jsonString := value.(string)
|
||||||
|
buf = append(buf, jsonString...)
|
||||||
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type encodePlanJSONCodecEitherFormatByteSlice struct{}
|
type encodePlanJSONCodecEitherFormatByteSlice struct{}
|
||||||
|
|
|
@ -1155,6 +1155,14 @@ func codecDecodeToTextFormat(codec Codec, m *Map, oid uint32, format int16, src
|
||||||
// PlanEncode returns an Encode plan for encoding value into PostgreSQL format for oid and format. If no plan can be
|
// PlanEncode returns an Encode plan for encoding value into PostgreSQL format for oid and format. If no plan can be
|
||||||
// found then nil is returned.
|
// found then nil is returned.
|
||||||
func (m *Map) PlanEncode(oid uint32, format int16, value interface{}) EncodePlan {
|
func (m *Map) PlanEncode(oid uint32, format int16, value interface{}) EncodePlan {
|
||||||
|
if format == TextFormatCode {
|
||||||
|
switch value.(type) {
|
||||||
|
case string:
|
||||||
|
return encodePlanStringToAnyTextFormat{}
|
||||||
|
case TextValuer:
|
||||||
|
return encodePlanTextValuerToAnyTextFormat{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var dt *Type
|
var dt *Type
|
||||||
|
|
||||||
|
@ -1187,6 +1195,27 @@ func (m *Map) PlanEncode(oid uint32, format int16, value interface{}) EncodePlan
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type encodePlanStringToAnyTextFormat struct{}
|
||||||
|
|
||||||
|
func (encodePlanStringToAnyTextFormat) Encode(value interface{}, buf []byte) (newBuf []byte, err error) {
|
||||||
|
s := value.(string)
|
||||||
|
return append(buf, s...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type encodePlanTextValuerToAnyTextFormat struct{}
|
||||||
|
|
||||||
|
func (encodePlanTextValuerToAnyTextFormat) Encode(value interface{}, buf []byte) (newBuf []byte, err error) {
|
||||||
|
t, err := value.(TextValuer).TextValue()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !t.Valid {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(buf, t.String...), nil
|
||||||
|
}
|
||||||
|
|
||||||
// TryWrapEncodePlanFunc is a function that tries to create a wrapper plan for value. If successful it returns a plan
|
// TryWrapEncodePlanFunc is a function that tries to create a wrapper plan for value. If successful it returns a plan
|
||||||
// that will convert the value passed to Encode and then call the next plan. nextValue is value as it will be converted
|
// that will convert the value passed to Encode and then call the next plan. nextValue is value as it will be converted
|
||||||
// by plan. It must be used to find another suitable EncodePlan. When it is found SetNext must be called on plan for it
|
// by plan. It must be used to find another suitable EncodePlan. When it is found SetNext must be called on plan for it
|
||||||
|
|
Loading…
Reference in New Issue