From 1b8f0016e9129f9e9b78a360b5087d454455a5b7 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Sat, 20 Apr 2019 19:20:00 -0500 Subject: [PATCH] Split pgtype into own repo --- batch.go | 2 +- batch_test.go | 2 +- bench_test.go | 2 +- conn.go | 2 +- conn_test.go | 2 +- example_custom_type_test.go | 2 +- fastpath.go | 2 +- go.mod | 7 +- go.sum | 5 + large_objects.go | 2 +- messages.go | 2 +- pgmock/pgmock.go | 2 +- pgtype/aclitem.go | 127 ---- pgtype/aclitem_array.go | 213 ------- pgtype/aclitem_array_test.go | 152 ----- pgtype/aclitem_test.go | 97 --- pgtype/array.go | 352 ----------- pgtype/array_test.go | 105 --- pgtype/bit.go | 37 -- pgtype/bit_test.go | 25 - pgtype/bool.go | 165 ----- pgtype/bool_array.go | 301 --------- pgtype/bool_array_test.go | 153 ----- pgtype/bool_test.go | 97 --- pgtype/box.go | 166 ----- pgtype/box_test.go | 34 - pgtype/bpchar.go | 68 -- pgtype/bpchar_array.go | 301 --------- pgtype/bpchar_array_test.go | 55 -- pgtype/bpchar_test.go | 51 -- pgtype/bytea.go | 157 ----- pgtype/bytea_array.go | 301 --------- pgtype/bytea_array_test.go | 120 ---- pgtype/bytea_test.go | 73 --- pgtype/cid.go | 61 -- pgtype/cid_test.go | 105 --- pgtype/cidr.go | 31 - pgtype/cidr_array.go | 330 ---------- pgtype/cidr_array_test.go | 165 ----- pgtype/circle.go | 151 ----- pgtype/circle_test.go | 16 - pgtype/convert.go | 424 ------------- pgtype/database_sql.go | 42 -- pgtype/date.go | 210 ------ pgtype/date_array.go | 302 --------- pgtype/date_array_test.go | 143 ----- pgtype/date_test.go | 118 ---- pgtype/daterange.go | 250 -------- pgtype/daterange_test.go | 67 -- pgtype/enum_array.go | 213 ------- pgtype/enum_array_test.go | 151 ----- pgtype/ext/satori-uuid/uuid.go | 162 ----- pgtype/ext/satori-uuid/uuid_test.go | 97 --- pgtype/ext/shopspring-numeric/decimal.go | 318 ---------- pgtype/ext/shopspring-numeric/decimal_test.go | 286 --------- pgtype/float4.go | 197 ------ pgtype/float4_array.go | 301 --------- pgtype/float4_array_test.go | 152 ----- pgtype/float4_test.go | 149 ----- pgtype/float8.go | 187 ------ pgtype/float8_array.go | 301 --------- pgtype/float8_array_test.go | 152 ----- pgtype/float8_test.go | 149 ----- pgtype/generic_binary.go | 39 -- pgtype/generic_text.go | 39 -- pgtype/hstore.go | 435 ------------- pgtype/hstore_array.go | 301 --------- pgtype/hstore_array_test.go | 199 ------ pgtype/hstore_test.go | 109 ---- pgtype/inet.go | 216 ------- pgtype/inet_array.go | 330 ---------- pgtype/inet_array_test.go | 165 ----- pgtype/inet_test.go | 115 ---- pgtype/int2.go | 209 ------ pgtype/int2_array.go | 329 ---------- pgtype/int2_array_test.go | 177 ------ pgtype/int2_test.go | 142 ----- pgtype/int4.go | 213 ------- pgtype/int4_array.go | 348 ---------- pgtype/int4_array_test.go | 198 ------ pgtype/int4_test.go | 143 ----- pgtype/int4range.go | 250 -------- pgtype/int4range_test.go | 28 - pgtype/int8.go | 199 ------ pgtype/int8_array.go | 329 ---------- pgtype/int8_array_test.go | 177 ------ pgtype/int8_test.go | 144 ----- pgtype/int8range.go | 250 -------- pgtype/int8range_test.go | 28 - pgtype/interval.go | 251 -------- pgtype/interval_test.go | 63 -- pgtype/json.go | 167 ----- pgtype/json_test.go | 136 ---- pgtype/jsonb.go | 70 -- pgtype/jsonb_test.go | 142 ----- pgtype/line.go | 149 ----- pgtype/line_test.go | 38 -- pgtype/lseg.go | 166 ----- pgtype/lseg_test.go | 22 - pgtype/macaddr.go | 155 ----- pgtype/macaddr_array.go | 302 --------- pgtype/macaddr_array_test.go | 105 --- pgtype/macaddr_test.go | 78 --- pgtype/name.go | 58 -- pgtype/name_test.go | 98 --- pgtype/numeric.go | 597 ------------------ pgtype/numeric_array.go | 385 ----------- pgtype/numeric_array_test.go | 160 ----- pgtype/numeric_test.go | 355 ----------- pgtype/numrange.go | 250 -------- pgtype/numrange_test.go | 46 -- pgtype/oid.go | 81 --- pgtype/oid_value.go | 55 -- pgtype/oid_value_test.go | 95 --- pgtype/path.go | 196 ------ pgtype/path_test.go | 29 - pgtype/pgtype.go | 417 ------------ pgtype/pgtype_test.go | 39 -- pgtype/pguint32.go | 162 ----- pgtype/point.go | 142 ----- pgtype/point_test.go | 16 - pgtype/polygon.go | 177 ------ pgtype/polygon_test.go | 22 - pgtype/qchar.go | 146 ----- pgtype/qchar_test.go | 143 ----- pgtype/range.go | 278 -------- pgtype/range_test.go | 177 ------ pgtype/record.go | 130 ---- pgtype/record_test.go | 184 ------ pgtype/testutil/testutil.go | 286 --------- pgtype/text.go | 164 ----- pgtype/text_array.go | 301 --------- pgtype/text_array_test.go | 152 ----- pgtype/text_test.go | 123 ---- pgtype/tid.go | 144 ----- pgtype/tid_test.go | 16 - pgtype/timestamp.go | 226 ------- pgtype/timestamp_array.go | 302 --------- pgtype/timestamp_array_test.go | 159 ----- pgtype/timestamp_test.go | 123 ---- pgtype/timestamptz.go | 222 ------- pgtype/timestamptz_array.go | 302 --------- pgtype/timestamptz_array_test.go | 159 ----- pgtype/timestamptz_test.go | 122 ---- pgtype/tsrange.go | 250 -------- pgtype/tsrange_test.go | 41 -- pgtype/tstzrange.go | 250 -------- pgtype/tstzrange_test.go | 41 -- pgtype/typed_array.go.erb | 305 --------- pgtype/typed_array_gen.sh | 25 - pgtype/typed_range.go.erb | 252 -------- pgtype/typed_range_gen.sh | 7 - pgtype/unknown.go | 44 -- pgtype/uuid.go | 186 ------ pgtype/uuid_array.go | 357 ----------- pgtype/uuid_array_test.go | 205 ------ pgtype/uuid_test.go | 104 --- pgtype/varbit.go | 133 ---- pgtype/varbit_test.go | 26 - pgtype/varchar.go | 58 -- pgtype/varchar_array.go | 301 --------- pgtype/varchar_array_test.go | 152 ----- pgtype/xid.go | 64 -- pgtype/xid_test.go | 105 --- query.go | 2 +- query_test.go | 4 +- replication.go | 2 +- stdlib/sql.go | 2 +- values.go | 2 +- 169 files changed, 23 insertions(+), 25502 deletions(-) delete mode 100644 pgtype/aclitem.go delete mode 100644 pgtype/aclitem_array.go delete mode 100644 pgtype/aclitem_array_test.go delete mode 100644 pgtype/aclitem_test.go delete mode 100644 pgtype/array.go delete mode 100644 pgtype/array_test.go delete mode 100644 pgtype/bit.go delete mode 100644 pgtype/bit_test.go delete mode 100644 pgtype/bool.go delete mode 100644 pgtype/bool_array.go delete mode 100644 pgtype/bool_array_test.go delete mode 100644 pgtype/bool_test.go delete mode 100644 pgtype/box.go delete mode 100644 pgtype/box_test.go delete mode 100644 pgtype/bpchar.go delete mode 100644 pgtype/bpchar_array.go delete mode 100644 pgtype/bpchar_array_test.go delete mode 100644 pgtype/bpchar_test.go delete mode 100644 pgtype/bytea.go delete mode 100644 pgtype/bytea_array.go delete mode 100644 pgtype/bytea_array_test.go delete mode 100644 pgtype/bytea_test.go delete mode 100644 pgtype/cid.go delete mode 100644 pgtype/cid_test.go delete mode 100644 pgtype/cidr.go delete mode 100644 pgtype/cidr_array.go delete mode 100644 pgtype/cidr_array_test.go delete mode 100644 pgtype/circle.go delete mode 100644 pgtype/circle_test.go delete mode 100644 pgtype/convert.go delete mode 100644 pgtype/database_sql.go delete mode 100644 pgtype/date.go delete mode 100644 pgtype/date_array.go delete mode 100644 pgtype/date_array_test.go delete mode 100644 pgtype/date_test.go delete mode 100644 pgtype/daterange.go delete mode 100644 pgtype/daterange_test.go delete mode 100644 pgtype/enum_array.go delete mode 100644 pgtype/enum_array_test.go delete mode 100644 pgtype/ext/satori-uuid/uuid.go delete mode 100644 pgtype/ext/satori-uuid/uuid_test.go delete mode 100644 pgtype/ext/shopspring-numeric/decimal.go delete mode 100644 pgtype/ext/shopspring-numeric/decimal_test.go delete mode 100644 pgtype/float4.go delete mode 100644 pgtype/float4_array.go delete mode 100644 pgtype/float4_array_test.go delete mode 100644 pgtype/float4_test.go delete mode 100644 pgtype/float8.go delete mode 100644 pgtype/float8_array.go delete mode 100644 pgtype/float8_array_test.go delete mode 100644 pgtype/float8_test.go delete mode 100644 pgtype/generic_binary.go delete mode 100644 pgtype/generic_text.go delete mode 100644 pgtype/hstore.go delete mode 100644 pgtype/hstore_array.go delete mode 100644 pgtype/hstore_array_test.go delete mode 100644 pgtype/hstore_test.go delete mode 100644 pgtype/inet.go delete mode 100644 pgtype/inet_array.go delete mode 100644 pgtype/inet_array_test.go delete mode 100644 pgtype/inet_test.go delete mode 100644 pgtype/int2.go delete mode 100644 pgtype/int2_array.go delete mode 100644 pgtype/int2_array_test.go delete mode 100644 pgtype/int2_test.go delete mode 100644 pgtype/int4.go delete mode 100644 pgtype/int4_array.go delete mode 100644 pgtype/int4_array_test.go delete mode 100644 pgtype/int4_test.go delete mode 100644 pgtype/int4range.go delete mode 100644 pgtype/int4range_test.go delete mode 100644 pgtype/int8.go delete mode 100644 pgtype/int8_array.go delete mode 100644 pgtype/int8_array_test.go delete mode 100644 pgtype/int8_test.go delete mode 100644 pgtype/int8range.go delete mode 100644 pgtype/int8range_test.go delete mode 100644 pgtype/interval.go delete mode 100644 pgtype/interval_test.go delete mode 100644 pgtype/json.go delete mode 100644 pgtype/json_test.go delete mode 100644 pgtype/jsonb.go delete mode 100644 pgtype/jsonb_test.go delete mode 100644 pgtype/line.go delete mode 100644 pgtype/line_test.go delete mode 100644 pgtype/lseg.go delete mode 100644 pgtype/lseg_test.go delete mode 100644 pgtype/macaddr.go delete mode 100644 pgtype/macaddr_array.go delete mode 100644 pgtype/macaddr_array_test.go delete mode 100644 pgtype/macaddr_test.go delete mode 100644 pgtype/name.go delete mode 100644 pgtype/name_test.go delete mode 100644 pgtype/numeric.go delete mode 100644 pgtype/numeric_array.go delete mode 100644 pgtype/numeric_array_test.go delete mode 100644 pgtype/numeric_test.go delete mode 100644 pgtype/numrange.go delete mode 100644 pgtype/numrange_test.go delete mode 100644 pgtype/oid.go delete mode 100644 pgtype/oid_value.go delete mode 100644 pgtype/oid_value_test.go delete mode 100644 pgtype/path.go delete mode 100644 pgtype/path_test.go delete mode 100644 pgtype/pgtype.go delete mode 100644 pgtype/pgtype_test.go delete mode 100644 pgtype/pguint32.go delete mode 100644 pgtype/point.go delete mode 100644 pgtype/point_test.go delete mode 100644 pgtype/polygon.go delete mode 100644 pgtype/polygon_test.go delete mode 100644 pgtype/qchar.go delete mode 100644 pgtype/qchar_test.go delete mode 100644 pgtype/range.go delete mode 100644 pgtype/range_test.go delete mode 100644 pgtype/record.go delete mode 100644 pgtype/record_test.go delete mode 100644 pgtype/testutil/testutil.go delete mode 100644 pgtype/text.go delete mode 100644 pgtype/text_array.go delete mode 100644 pgtype/text_array_test.go delete mode 100644 pgtype/text_test.go delete mode 100644 pgtype/tid.go delete mode 100644 pgtype/tid_test.go delete mode 100644 pgtype/timestamp.go delete mode 100644 pgtype/timestamp_array.go delete mode 100644 pgtype/timestamp_array_test.go delete mode 100644 pgtype/timestamp_test.go delete mode 100644 pgtype/timestamptz.go delete mode 100644 pgtype/timestamptz_array.go delete mode 100644 pgtype/timestamptz_array_test.go delete mode 100644 pgtype/timestamptz_test.go delete mode 100644 pgtype/tsrange.go delete mode 100644 pgtype/tsrange_test.go delete mode 100644 pgtype/tstzrange.go delete mode 100644 pgtype/tstzrange_test.go delete mode 100644 pgtype/typed_array.go.erb delete mode 100644 pgtype/typed_array_gen.sh delete mode 100644 pgtype/typed_range.go.erb delete mode 100644 pgtype/typed_range_gen.sh delete mode 100644 pgtype/unknown.go delete mode 100644 pgtype/uuid.go delete mode 100644 pgtype/uuid_array.go delete mode 100644 pgtype/uuid_array_test.go delete mode 100644 pgtype/uuid_test.go delete mode 100644 pgtype/varbit.go delete mode 100644 pgtype/varbit_test.go delete mode 100644 pgtype/varchar.go delete mode 100644 pgtype/varchar_array.go delete mode 100644 pgtype/varchar_array_test.go delete mode 100644 pgtype/xid.go delete mode 100644 pgtype/xid_test.go diff --git a/batch.go b/batch.go index a2d0ad28..1362a02e 100644 --- a/batch.go +++ b/batch.go @@ -4,7 +4,7 @@ import ( "context" "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4/pgtype" + "github.com/jackc/pgtype" errors "golang.org/x/xerrors" ) diff --git a/batch_test.go b/batch_test.go index a425455b..c93c3dfa 100644 --- a/batch_test.go +++ b/batch_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/jackc/pgconn" + "github.com/jackc/pgtype" "github.com/jackc/pgx/v4" - "github.com/jackc/pgx/v4/pgtype" ) func TestConnBeginBatch(t *testing.T) { diff --git a/bench_test.go b/bench_test.go index 0d89bf72..39f3f324 100644 --- a/bench_test.go +++ b/bench_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" + "github.com/jackc/pgtype" "github.com/jackc/pgx/v4" - "github.com/jackc/pgx/v4/pgtype" ) func BenchmarkPointerPointerWithNullValues(b *testing.B) { diff --git a/conn.go b/conn.go index 496283dd..15a19501 100644 --- a/conn.go +++ b/conn.go @@ -13,7 +13,7 @@ import ( "github.com/jackc/pgconn" "github.com/jackc/pgproto3/v2" - "github.com/jackc/pgx/v4/pgtype" + "github.com/jackc/pgtype" ) const ( diff --git a/conn_test.go b/conn_test.go index bdb3de8d..18c48e7c 100644 --- a/conn_test.go +++ b/conn_test.go @@ -9,8 +9,8 @@ import ( "time" "github.com/jackc/pgconn" + "github.com/jackc/pgtype" "github.com/jackc/pgx/v4" - "github.com/jackc/pgx/v4/pgtype" "github.com/stretchr/testify/require" errors "golang.org/x/xerrors" ) diff --git a/example_custom_type_test.go b/example_custom_type_test.go index f30c1b19..1cc16401 100644 --- a/example_custom_type_test.go +++ b/example_custom_type_test.go @@ -7,8 +7,8 @@ import ( "regexp" "strconv" + "github.com/jackc/pgtype" "github.com/jackc/pgx/v4" - "github.com/jackc/pgx/v4/pgtype" errors "golang.org/x/xerrors" ) diff --git a/fastpath.go b/fastpath.go index 72d597d0..e80d9014 100644 --- a/fastpath.go +++ b/fastpath.go @@ -5,7 +5,7 @@ import ( "github.com/jackc/pgio" "github.com/jackc/pgproto3" - "github.com/jackc/pgx/v4/pgtype" + "github.com/jackc/pgtype" ) func newFastpath(cn *Conn) *fastpath { diff --git a/go.mod b/go.mod index 25a7dcaa..80d3e26e 100644 --- a/go.mod +++ b/go.mod @@ -8,18 +8,15 @@ require ( github.com/jackc/pgio v1.0.0 github.com/jackc/pgproto3 v1.1.0 github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db + github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0 github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b - github.com/kr/pretty v0.1.0 // indirect - github.com/lib/pq v1.0.0 + github.com/lib/pq v1.1.0 github.com/pkg/errors v0.8.1 github.com/rs/zerolog v1.13.0 github.com/satori/go.uuid v1.2.0 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 github.com/sirupsen/logrus v1.4.1 github.com/stretchr/testify v1.3.0 - go.uber.org/atomic v1.3.2 // indirect - go.uber.org/multierr v1.1.0 // indirect go.uber.org/zap v1.9.1 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373 - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) diff --git a/go.sum b/go.sum index cca95bdf..9d1aae79 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,10 @@ github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db h1:UpaKn/gYxzH6/zWyRQH1S260zvKqwJJ4h8+Kf09ooh0= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0 h1:mX93v750WifMD1htCt7vqeolcnpaG1gz8URVGjSzcUM= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgx v3.3.0+incompatible h1:Wa90/+qsITBAPkAZjiByeIGHFcj3Ztu+VzrrIpHjL90= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b h1:cIcUpcEP55F/QuZWEtXyqHoWk+IV4TBiLjtBkeq/Q1c= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= @@ -27,6 +31,7 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/large_objects.go b/large_objects.go index 4f60f6be..1a5f32be 100644 --- a/large_objects.go +++ b/large_objects.go @@ -4,7 +4,7 @@ import ( "context" "io" - "github.com/jackc/pgx/v4/pgtype" + "github.com/jackc/pgtype" ) // LargeObjects is a structure used to access the large objects API. It is only diff --git a/messages.go b/messages.go index 5b8d3da5..45dda1ad 100644 --- a/messages.go +++ b/messages.go @@ -7,7 +7,7 @@ import ( "time" "github.com/jackc/pgio" - "github.com/jackc/pgx/v4/pgtype" + "github.com/jackc/pgtype" ) const ( diff --git a/pgmock/pgmock.go b/pgmock/pgmock.go index deac5137..366c3c9c 100644 --- a/pgmock/pgmock.go +++ b/pgmock/pgmock.go @@ -8,7 +8,7 @@ import ( errors "golang.org/x/xerrors" "github.com/jackc/pgproto3/v2" - "github.com/jackc/pgx/v4/pgtype" + "github.com/jackc/pgtype" ) type Server struct { diff --git a/pgtype/aclitem.go b/pgtype/aclitem.go deleted file mode 100644 index c801eb83..00000000 --- a/pgtype/aclitem.go +++ /dev/null @@ -1,127 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - - errors "golang.org/x/xerrors" -) - -// ACLItem is used for PostgreSQL's aclitem data type. A sample aclitem -// might look like this: -// -// postgres=arwdDxt/postgres -// -// Note, however, that because the user/role name part of an aclitem is -// an identifier, it follows all the usual formatting rules for SQL -// identifiers: if it contains spaces and other special characters, -// it should appear in double-quotes: -// -// postgres=arwdDxt/"role with spaces" -// -type ACLItem struct { - String string - Status Status -} - -func (dst *ACLItem) Set(src interface{}) error { - switch value := src.(type) { - case string: - *dst = ACLItem{String: value, Status: Present} - case *string: - if value == nil { - *dst = ACLItem{Status: Null} - } else { - *dst = ACLItem{String: *value, Status: Present} - } - default: - if originalSrc, ok := underlyingStringType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to ACLItem", value) - } - - return nil -} - -func (dst *ACLItem) Get() interface{} { - switch dst.Status { - case Present: - return dst.String - case Null: - return nil - default: - return dst.Status - } -} - -func (src *ACLItem) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *string: - *v = src.String - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *ACLItem) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = ACLItem{Status: Null} - return nil - } - - *dst = ACLItem{String: string(src), Status: Present} - return nil -} - -func (src *ACLItem) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, src.String...), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *ACLItem) Scan(src interface{}) error { - if src == nil { - *dst = ACLItem{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *ACLItem) Value() (driver.Value, error) { - switch src.Status { - case Present: - return src.String, nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} diff --git a/pgtype/aclitem_array.go b/pgtype/aclitem_array.go deleted file mode 100644 index c8421153..00000000 --- a/pgtype/aclitem_array.go +++ /dev/null @@ -1,213 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - - errors "golang.org/x/xerrors" -) - -type ACLItemArray struct { - Elements []ACLItem - Dimensions []ArrayDimension - Status Status -} - -func (dst *ACLItemArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = ACLItemArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []string: - if value == nil { - *dst = ACLItemArray{Status: Null} - } else if len(value) == 0 { - *dst = ACLItemArray{Status: Present} - } else { - elements := make([]ACLItem, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = ACLItemArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to ACLItemArray", value) - } - - return nil -} - -func (dst *ACLItemArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *ACLItemArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]string: - *v = make([]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *ACLItemArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = ACLItemArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []ACLItem - - if len(uta.Elements) > 0 { - elements = make([]ACLItem, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem ACLItem - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = ACLItemArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (src *ACLItemArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *ACLItemArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *ACLItemArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/aclitem_array_test.go b/pgtype/aclitem_array_test.go deleted file mode 100644 index 5f16ab28..00000000 --- a/pgtype/aclitem_array_test.go +++ /dev/null @@ -1,152 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestACLItemArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "aclitem[]", []interface{}{ - &pgtype.ACLItemArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.ACLItemArray{ - Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.ACLItemArray{Status: pgtype.Null}, - &pgtype.ACLItemArray{ - Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - {String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Status: pgtype.Present}, - {String: "=r/postgres", Status: pgtype.Present}, - {Status: pgtype.Null}, - {String: "=r/postgres", Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.ACLItemArray{ - Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestACLItemArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.ACLItemArray - }{ - { - source: []string{"=r/postgres"}, - result: pgtype.ACLItemArray{ - Elements: []pgtype.ACLItem{{String: "=r/postgres", Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]string)(nil)), - result: pgtype.ACLItemArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.ACLItemArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestACLItemArrayAssignTo(t *testing.T) { - var stringSlice []string - type _stringSlice []string - var namedStringSlice _stringSlice - - simpleTests := []struct { - src pgtype.ACLItemArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.ACLItemArray{ - Elements: []pgtype.ACLItem{{String: "=r/postgres", Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &stringSlice, - expected: []string{"=r/postgres"}, - }, - { - src: pgtype.ACLItemArray{ - Elements: []pgtype.ACLItem{{String: "=r/postgres", Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &namedStringSlice, - expected: _stringSlice{"=r/postgres"}, - }, - { - src: pgtype.ACLItemArray{Status: pgtype.Null}, - dst: &stringSlice, - expected: (([]string)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.ACLItemArray - dst interface{} - }{ - { - src: pgtype.ACLItemArray{ - Elements: []pgtype.ACLItem{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &stringSlice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/aclitem_test.go b/pgtype/aclitem_test.go deleted file mode 100644 index 92dfc7a5..00000000 --- a/pgtype/aclitem_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestACLItemTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "aclitem", []interface{}{ - &pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - &pgtype.ACLItem{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Status: pgtype.Present}, - &pgtype.ACLItem{Status: pgtype.Null}, - }) -} - -func TestACLItemSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.ACLItem - }{ - {source: "postgres=arwdDxt/postgres", result: pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Status: pgtype.Present}}, - {source: (*string)(nil), result: pgtype.ACLItem{Status: pgtype.Null}}, - } - - for i, tt := range successfulTests { - var d pgtype.ACLItem - err := d.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if d != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, d) - } - } -} - -func TestACLItemAssignTo(t *testing.T) { - var s string - var ps *string - - simpleTests := []struct { - src pgtype.ACLItem - dst interface{} - expected interface{} - }{ - {src: pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, dst: &s, expected: "postgres=arwdDxt/postgres"}, - {src: pgtype.ACLItem{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.ACLItem - dst interface{} - expected interface{} - }{ - {src: pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, dst: &ps, expected: "postgres=arwdDxt/postgres"}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.ACLItem - dst interface{} - }{ - {src: pgtype.ACLItem{Status: pgtype.Null}, dst: &s}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/array.go b/pgtype/array.go deleted file mode 100644 index 69456782..00000000 --- a/pgtype/array.go +++ /dev/null @@ -1,352 +0,0 @@ -package pgtype - -import ( - "bytes" - "encoding/binary" - "io" - "strconv" - "strings" - "unicode" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -// Information on the internals of PostgreSQL arrays can be found in -// src/include/utils/array.h and src/backend/utils/adt/arrayfuncs.c. Of -// particular interest is the array_send function. - -type ArrayHeader struct { - ContainsNull bool - ElementOID int32 - Dimensions []ArrayDimension -} - -type ArrayDimension struct { - Length int32 - LowerBound int32 -} - -func (dst *ArrayHeader) DecodeBinary(ci *ConnInfo, src []byte) (int, error) { - if len(src) < 12 { - return 0, errors.Errorf("array header too short: %d", len(src)) - } - - rp := 0 - - numDims := int(binary.BigEndian.Uint32(src[rp:])) - rp += 4 - - dst.ContainsNull = binary.BigEndian.Uint32(src[rp:]) == 1 - rp += 4 - - dst.ElementOID = int32(binary.BigEndian.Uint32(src[rp:])) - rp += 4 - - if numDims > 0 { - dst.Dimensions = make([]ArrayDimension, numDims) - } - if len(src) < 12+numDims*8 { - return 0, errors.Errorf("array header too short for %d dimensions: %d", numDims, len(src)) - } - for i := range dst.Dimensions { - dst.Dimensions[i].Length = int32(binary.BigEndian.Uint32(src[rp:])) - rp += 4 - - dst.Dimensions[i].LowerBound = int32(binary.BigEndian.Uint32(src[rp:])) - rp += 4 - } - - return rp, nil -} - -func (src *ArrayHeader) EncodeBinary(ci *ConnInfo, buf []byte) []byte { - buf = pgio.AppendInt32(buf, int32(len(src.Dimensions))) - - var containsNull int32 - if src.ContainsNull { - containsNull = 1 - } - buf = pgio.AppendInt32(buf, containsNull) - - buf = pgio.AppendInt32(buf, src.ElementOID) - - for i := range src.Dimensions { - buf = pgio.AppendInt32(buf, src.Dimensions[i].Length) - buf = pgio.AppendInt32(buf, src.Dimensions[i].LowerBound) - } - - return buf -} - -type UntypedTextArray struct { - Elements []string - Dimensions []ArrayDimension -} - -func ParseUntypedTextArray(src string) (*UntypedTextArray, error) { - dst := &UntypedTextArray{} - - buf := bytes.NewBufferString(src) - - skipWhitespace(buf) - - r, _, err := buf.ReadRune() - if err != nil { - return nil, errors.Errorf("invalid array: %v", err) - } - - var explicitDimensions []ArrayDimension - - // Array has explicit dimensions - if r == '[' { - buf.UnreadRune() - - for { - r, _, err = buf.ReadRune() - if err != nil { - return nil, errors.Errorf("invalid array: %v", err) - } - - if r == '=' { - break - } else if r != '[' { - return nil, errors.Errorf("invalid array, expected '[' or '=' got %v", r) - } - - lower, err := arrayParseInteger(buf) - if err != nil { - return nil, errors.Errorf("invalid array: %v", err) - } - - r, _, err = buf.ReadRune() - if err != nil { - return nil, errors.Errorf("invalid array: %v", err) - } - - if r != ':' { - return nil, errors.Errorf("invalid array, expected ':' got %v", r) - } - - upper, err := arrayParseInteger(buf) - if err != nil { - return nil, errors.Errorf("invalid array: %v", err) - } - - r, _, err = buf.ReadRune() - if err != nil { - return nil, errors.Errorf("invalid array: %v", err) - } - - if r != ']' { - return nil, errors.Errorf("invalid array, expected ']' got %v", r) - } - - explicitDimensions = append(explicitDimensions, ArrayDimension{LowerBound: lower, Length: upper - lower + 1}) - } - - r, _, err = buf.ReadRune() - if err != nil { - return nil, errors.Errorf("invalid array: %v", err) - } - } - - if r != '{' { - return nil, errors.Errorf("invalid array, expected '{': %v", err) - } - - implicitDimensions := []ArrayDimension{{LowerBound: 1, Length: 0}} - - // Consume all initial opening brackets. This provides number of dimensions. - for { - r, _, err = buf.ReadRune() - if err != nil { - return nil, errors.Errorf("invalid array: %v", err) - } - - if r == '{' { - implicitDimensions[len(implicitDimensions)-1].Length = 1 - implicitDimensions = append(implicitDimensions, ArrayDimension{LowerBound: 1}) - } else { - buf.UnreadRune() - break - } - } - currentDim := len(implicitDimensions) - 1 - counterDim := currentDim - - for { - r, _, err = buf.ReadRune() - if err != nil { - return nil, errors.Errorf("invalid array: %v", err) - } - - switch r { - case '{': - if currentDim == counterDim { - implicitDimensions[currentDim].Length++ - } - currentDim++ - case ',': - case '}': - currentDim-- - if currentDim < counterDim { - counterDim = currentDim - } - default: - buf.UnreadRune() - value, err := arrayParseValue(buf) - if err != nil { - return nil, errors.Errorf("invalid array value: %v", err) - } - if currentDim == counterDim { - implicitDimensions[currentDim].Length++ - } - dst.Elements = append(dst.Elements, value) - } - - if currentDim < 0 { - break - } - } - - skipWhitespace(buf) - - if buf.Len() > 0 { - return nil, errors.Errorf("unexpected trailing data: %v", buf.String()) - } - - if len(dst.Elements) == 0 { - dst.Dimensions = nil - } else if len(explicitDimensions) > 0 { - dst.Dimensions = explicitDimensions - } else { - dst.Dimensions = implicitDimensions - } - - return dst, nil -} - -func skipWhitespace(buf *bytes.Buffer) { - var r rune - var err error - for r, _, _ = buf.ReadRune(); unicode.IsSpace(r); r, _, _ = buf.ReadRune() { - } - - if err != io.EOF { - buf.UnreadRune() - } -} - -func arrayParseValue(buf *bytes.Buffer) (string, error) { - r, _, err := buf.ReadRune() - if err != nil { - return "", err - } - if r == '"' { - return arrayParseQuotedValue(buf) - } - buf.UnreadRune() - - s := &bytes.Buffer{} - - for { - r, _, err := buf.ReadRune() - if err != nil { - return "", err - } - - switch r { - case ',', '}': - buf.UnreadRune() - return s.String(), nil - } - - s.WriteRune(r) - } -} - -func arrayParseQuotedValue(buf *bytes.Buffer) (string, error) { - s := &bytes.Buffer{} - - for { - r, _, err := buf.ReadRune() - if err != nil { - return "", err - } - - switch r { - case '\\': - r, _, err = buf.ReadRune() - if err != nil { - return "", err - } - case '"': - r, _, err = buf.ReadRune() - if err != nil { - return "", err - } - buf.UnreadRune() - return s.String(), nil - } - s.WriteRune(r) - } -} - -func arrayParseInteger(buf *bytes.Buffer) (int32, error) { - s := &bytes.Buffer{} - - for { - r, _, err := buf.ReadRune() - if err != nil { - return 0, err - } - - if '0' <= r && r <= '9' { - s.WriteRune(r) - } else { - buf.UnreadRune() - n, err := strconv.ParseInt(s.String(), 10, 32) - if err != nil { - return 0, err - } - return int32(n), nil - } - } -} - -func EncodeTextArrayDimensions(buf []byte, dimensions []ArrayDimension) []byte { - var customDimensions bool - for _, dim := range dimensions { - if dim.LowerBound != 1 { - customDimensions = true - } - } - - if !customDimensions { - return buf - } - - for _, dim := range dimensions { - buf = append(buf, '[') - buf = append(buf, strconv.FormatInt(int64(dim.LowerBound), 10)...) - buf = append(buf, ':') - buf = append(buf, strconv.FormatInt(int64(dim.LowerBound+dim.Length-1), 10)...) - buf = append(buf, ']') - } - - return append(buf, '=') -} - -var quoteArrayReplacer = strings.NewReplacer(`\`, `\\`, `"`, `\"`) - -func quoteArrayElement(src string) string { - return `"` + quoteArrayReplacer.Replace(src) + `"` -} - -func QuoteArrayElementIfNeeded(src string) string { - if src == "" || (len(src) == 4 && strings.ToLower(src) == "null") || src[0] == ' ' || src[len(src)-1] == ' ' || strings.ContainsAny(src, `{},"\`) { - return quoteArrayElement(src) - } - return src -} diff --git a/pgtype/array_test.go b/pgtype/array_test.go deleted file mode 100644 index d17d753c..00000000 --- a/pgtype/array_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" -) - -func TestParseUntypedTextArray(t *testing.T) { - tests := []struct { - source string - result pgtype.UntypedTextArray - }{ - { - source: "{}", - result: pgtype.UntypedTextArray{ - Elements: nil, - Dimensions: nil, - }, - }, - { - source: "{1}", - result: pgtype.UntypedTextArray{ - Elements: []string{"1"}, - Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}}, - }, - }, - { - source: "{a,b}", - result: pgtype.UntypedTextArray{ - Elements: []string{"a", "b"}, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - }, - }, - { - source: `{"NULL"}`, - result: pgtype.UntypedTextArray{ - Elements: []string{"NULL"}, - Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}}, - }, - }, - { - source: `{""}`, - result: pgtype.UntypedTextArray{ - Elements: []string{""}, - Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}}, - }, - }, - { - source: `{"He said, \"Hello.\""}`, - result: pgtype.UntypedTextArray{ - Elements: []string{`He said, "Hello."`}, - Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}}, - }, - }, - { - source: "{{a,b},{c,d},{e,f}}", - result: pgtype.UntypedTextArray{ - Elements: []string{"a", "b", "c", "d", "e", "f"}, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - }, - }, - { - source: "{{{a,b},{c,d},{e,f}},{{a,b},{c,d},{e,f}}}", - result: pgtype.UntypedTextArray{ - Elements: []string{"a", "b", "c", "d", "e", "f", "a", "b", "c", "d", "e", "f"}, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 1}, - {Length: 3, LowerBound: 1}, - {Length: 2, LowerBound: 1}, - }, - }, - }, - { - source: "[4:4]={1}", - result: pgtype.UntypedTextArray{ - Elements: []string{"1"}, - Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 4}}, - }, - }, - { - source: "[4:5][2:3]={{a,b},{c,d}}", - result: pgtype.UntypedTextArray{ - Elements: []string{"a", "b", "c", "d"}, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - }, - }, - } - - for i, tt := range tests { - r, err := pgtype.ParseUntypedTextArray(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - continue - } - - if !reflect.DeepEqual(*r, tt.result) { - t.Errorf("%d: expected %+v to be parsed to %+v, but it was %+v", i, tt.source, tt.result, *r) - } - } -} diff --git a/pgtype/bit.go b/pgtype/bit.go deleted file mode 100644 index f892cee5..00000000 --- a/pgtype/bit.go +++ /dev/null @@ -1,37 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" -) - -type Bit Varbit - -func (dst *Bit) Set(src interface{}) error { - return (*Varbit)(dst).Set(src) -} - -func (dst *Bit) Get() interface{} { - return (*Varbit)(dst).Get() -} - -func (src *Bit) AssignTo(dst interface{}) error { - return (*Varbit)(src).AssignTo(dst) -} - -func (dst *Bit) DecodeBinary(ci *ConnInfo, src []byte) error { - return (*Varbit)(dst).DecodeBinary(ci, src) -} - -func (src *Bit) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*Varbit)(src).EncodeBinary(ci, buf) -} - -// Scan implements the database/sql Scanner interface. -func (dst *Bit) Scan(src interface{}) error { - return (*Varbit)(dst).Scan(src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Bit) Value() (driver.Value, error) { - return (*Varbit)(src).Value() -} diff --git a/pgtype/bit_test.go b/pgtype/bit_test.go deleted file mode 100644 index 05729323..00000000 --- a/pgtype/bit_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package pgtype_test - -import ( - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestBitTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "bit(40)", []interface{}{ - &pgtype.Varbit{Bytes: []byte{0, 0, 0, 0, 0}, Len: 40, Status: pgtype.Present}, - &pgtype.Varbit{Bytes: []byte{0, 1, 128, 254, 255}, Len: 40, Status: pgtype.Present}, - &pgtype.Varbit{Status: pgtype.Null}, - }) -} - -func TestBitNormalize(t *testing.T) { - testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{ - { - SQL: "select B'111111111'", - Value: &pgtype.Bit{Bytes: []byte{255, 128}, Len: 9, Status: pgtype.Present}, - }, - }) -} diff --git a/pgtype/bool.go b/pgtype/bool.go deleted file mode 100644 index f622061b..00000000 --- a/pgtype/bool.go +++ /dev/null @@ -1,165 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "strconv" - - errors "golang.org/x/xerrors" -) - -type Bool struct { - Bool bool - Status Status -} - -func (dst *Bool) Set(src interface{}) error { - if src == nil { - *dst = Bool{Status: Null} - return nil - } - - switch value := src.(type) { - case bool: - *dst = Bool{Bool: value, Status: Present} - case string: - bb, err := strconv.ParseBool(value) - if err != nil { - return err - } - *dst = Bool{Bool: bb, Status: Present} - default: - if originalSrc, ok := underlyingBoolType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Bool", value) - } - - return nil -} - -func (dst *Bool) Get() interface{} { - switch dst.Status { - case Present: - return dst.Bool - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Bool) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *bool: - *v = src.Bool - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Bool) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Bool{Status: Null} - return nil - } - - if len(src) != 1 { - return errors.Errorf("invalid length for bool: %v", len(src)) - } - - *dst = Bool{Bool: src[0] == 't', Status: Present} - return nil -} - -func (dst *Bool) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Bool{Status: Null} - return nil - } - - if len(src) != 1 { - return errors.Errorf("invalid length for bool: %v", len(src)) - } - - *dst = Bool{Bool: src[0] == 1, Status: Present} - return nil -} - -func (src *Bool) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if src.Bool { - buf = append(buf, 't') - } else { - buf = append(buf, 'f') - } - - return buf, nil -} - -func (src *Bool) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if src.Bool { - buf = append(buf, 1) - } else { - buf = append(buf, 0) - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Bool) Scan(src interface{}) error { - if src == nil { - *dst = Bool{Status: Null} - return nil - } - - switch src := src.(type) { - case bool: - *dst = Bool{Bool: src, Status: Present} - return nil - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Bool) Value() (driver.Value, error) { - switch src.Status { - case Present: - return src.Bool, nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} diff --git a/pgtype/bool_array.go b/pgtype/bool_array.go deleted file mode 100644 index 3dde8dc0..00000000 --- a/pgtype/bool_array.go +++ /dev/null @@ -1,301 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type BoolArray struct { - Elements []Bool - Dimensions []ArrayDimension - Status Status -} - -func (dst *BoolArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = BoolArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []bool: - if value == nil { - *dst = BoolArray{Status: Null} - } else if len(value) == 0 { - *dst = BoolArray{Status: Present} - } else { - elements := make([]Bool, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = BoolArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to BoolArray", value) - } - - return nil -} - -func (dst *BoolArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *BoolArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]bool: - *v = make([]bool, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *BoolArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = BoolArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Bool - - if len(uta.Elements) > 0 { - elements = make([]Bool, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Bool - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = BoolArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *BoolArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = BoolArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = BoolArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Bool, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = BoolArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *BoolArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *BoolArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("bool"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "bool") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *BoolArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *BoolArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/bool_array_test.go b/pgtype/bool_array_test.go deleted file mode 100644 index 6d2d7c06..00000000 --- a/pgtype/bool_array_test.go +++ /dev/null @@ -1,153 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestBoolArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "bool[]", []interface{}{ - &pgtype.BoolArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.BoolArray{ - Elements: []pgtype.Bool{ - {Bool: true, Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.BoolArray{Status: pgtype.Null}, - &pgtype.BoolArray{ - Elements: []pgtype.Bool{ - {Bool: true, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Bool: false, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.BoolArray{ - Elements: []pgtype.Bool{ - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestBoolArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.BoolArray - }{ - { - source: []bool{true}, - result: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]bool)(nil)), - result: pgtype.BoolArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.BoolArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestBoolArrayAssignTo(t *testing.T) { - var boolSlice []bool - type _boolSlice []bool - var namedBoolSlice _boolSlice - - simpleTests := []struct { - src pgtype.BoolArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &boolSlice, - expected: []bool{true}, - }, - { - src: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &namedBoolSlice, - expected: _boolSlice{true}, - }, - { - src: pgtype.BoolArray{Status: pgtype.Null}, - dst: &boolSlice, - expected: (([]bool)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.BoolArray - dst interface{} - }{ - { - src: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &boolSlice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } - -} diff --git a/pgtype/bool_test.go b/pgtype/bool_test.go deleted file mode 100644 index 5228e280..00000000 --- a/pgtype/bool_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestBoolTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "bool", []interface{}{ - &pgtype.Bool{Bool: false, Status: pgtype.Present}, - &pgtype.Bool{Bool: true, Status: pgtype.Present}, - &pgtype.Bool{Bool: false, Status: pgtype.Null}, - }) -} - -func TestBoolSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Bool - }{ - {source: true, result: pgtype.Bool{Bool: true, Status: pgtype.Present}}, - {source: false, result: pgtype.Bool{Bool: false, Status: pgtype.Present}}, - {source: "true", result: pgtype.Bool{Bool: true, Status: pgtype.Present}}, - {source: "false", result: pgtype.Bool{Bool: false, Status: pgtype.Present}}, - {source: "t", result: pgtype.Bool{Bool: true, Status: pgtype.Present}}, - {source: "f", result: pgtype.Bool{Bool: false, Status: pgtype.Present}}, - {source: _bool(true), result: pgtype.Bool{Bool: true, Status: pgtype.Present}}, - {source: _bool(false), result: pgtype.Bool{Bool: false, Status: pgtype.Present}}, - {source: nil, result: pgtype.Bool{Status: pgtype.Null}}, - } - - for i, tt := range successfulTests { - var r pgtype.Bool - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestBoolAssignTo(t *testing.T) { - var b bool - var _b _bool - var pb *bool - var _pb *_bool - - simpleTests := []struct { - src pgtype.Bool - dst interface{} - expected interface{} - }{ - {src: pgtype.Bool{Bool: false, Status: pgtype.Present}, dst: &b, expected: false}, - {src: pgtype.Bool{Bool: true, Status: pgtype.Present}, dst: &b, expected: true}, - {src: pgtype.Bool{Bool: false, Status: pgtype.Present}, dst: &_b, expected: _bool(false)}, - {src: pgtype.Bool{Bool: true, Status: pgtype.Present}, dst: &_b, expected: _bool(true)}, - {src: pgtype.Bool{Bool: false, Status: pgtype.Null}, dst: &pb, expected: ((*bool)(nil))}, - {src: pgtype.Bool{Bool: false, Status: pgtype.Null}, dst: &_pb, expected: ((*_bool)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.Bool - dst interface{} - expected interface{} - }{ - {src: pgtype.Bool{Bool: true, Status: pgtype.Present}, dst: &pb, expected: true}, - {src: pgtype.Bool{Bool: true, Status: pgtype.Present}, dst: &_pb, expected: _bool(true)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } -} diff --git a/pgtype/box.go b/pgtype/box.go deleted file mode 100644 index ce5300e5..00000000 --- a/pgtype/box.go +++ /dev/null @@ -1,166 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "fmt" - "math" - "strconv" - "strings" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Box struct { - P [2]Vec2 - Status Status -} - -func (dst *Box) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Box", src) -} - -func (dst *Box) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Box) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Box) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Box{Status: Null} - return nil - } - - if len(src) < 11 { - return errors.Errorf("invalid length for Box: %v", len(src)) - } - - str := string(src[1:]) - - var end int - end = strings.IndexByte(str, ',') - - x1, err := strconv.ParseFloat(str[:end], 64) - if err != nil { - return err - } - - str = str[end+1:] - end = strings.IndexByte(str, ')') - - y1, err := strconv.ParseFloat(str[:end], 64) - if err != nil { - return err - } - - str = str[end+3:] - end = strings.IndexByte(str, ',') - - x2, err := strconv.ParseFloat(str[:end], 64) - if err != nil { - return err - } - - str = str[end+1 : len(str)-1] - - y2, err := strconv.ParseFloat(str, 64) - if err != nil { - return err - } - - *dst = Box{P: [2]Vec2{{x1, y1}, {x2, y2}}, Status: Present} - return nil -} - -func (dst *Box) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Box{Status: Null} - return nil - } - - if len(src) != 32 { - return errors.Errorf("invalid length for Box: %v", len(src)) - } - - x1 := binary.BigEndian.Uint64(src) - y1 := binary.BigEndian.Uint64(src[8:]) - x2 := binary.BigEndian.Uint64(src[16:]) - y2 := binary.BigEndian.Uint64(src[24:]) - - *dst = Box{ - P: [2]Vec2{ - {math.Float64frombits(x1), math.Float64frombits(y1)}, - {math.Float64frombits(x2), math.Float64frombits(y2)}, - }, - Status: Present, - } - return nil -} - -func (src *Box) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = append(buf, fmt.Sprintf(`(%s,%s),(%s,%s)`, - strconv.FormatFloat(src.P[0].X, 'f', -1, 64), - strconv.FormatFloat(src.P[0].Y, 'f', -1, 64), - strconv.FormatFloat(src.P[1].X, 'f', -1, 64), - strconv.FormatFloat(src.P[1].Y, 'f', -1, 64), - )...) - return buf, nil -} - -func (src *Box) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = pgio.AppendUint64(buf, math.Float64bits(src.P[0].X)) - buf = pgio.AppendUint64(buf, math.Float64bits(src.P[0].Y)) - buf = pgio.AppendUint64(buf, math.Float64bits(src.P[1].X)) - buf = pgio.AppendUint64(buf, math.Float64bits(src.P[1].Y)) - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Box) Scan(src interface{}) error { - if src == nil { - *dst = Box{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Box) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/box_test.go b/pgtype/box_test.go deleted file mode 100644 index aad10262..00000000 --- a/pgtype/box_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package pgtype_test - -import ( - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestBoxTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "box", []interface{}{ - &pgtype.Box{ - P: [2]pgtype.Vec2{{7.1, 5.2345678}, {3.14, 1.678}}, - Status: pgtype.Present, - }, - &pgtype.Box{ - P: [2]pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}}, - Status: pgtype.Present, - }, - &pgtype.Box{Status: pgtype.Null}, - }) -} - -func TestBoxNormalize(t *testing.T) { - testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{ - { - SQL: "select '3.14, 1.678, 7.1, 5.234'::box", - Value: &pgtype.Box{ - P: [2]pgtype.Vec2{{7.1, 5.234}, {3.14, 1.678}}, - Status: pgtype.Present, - }, - }, - }) -} diff --git a/pgtype/bpchar.go b/pgtype/bpchar.go deleted file mode 100644 index 21263184..00000000 --- a/pgtype/bpchar.go +++ /dev/null @@ -1,68 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" -) - -// BPChar is fixed-length, blank padded char type -// character(n), char(n) -type BPChar Text - -// Set converts from src to dst. -func (dst *BPChar) Set(src interface{}) error { - return (*Text)(dst).Set(src) -} - -// Get returns underlying value -func (dst *BPChar) Get() interface{} { - return (*Text)(dst).Get() -} - -// AssignTo assigns from src to dst. -func (src *BPChar) AssignTo(dst interface{}) error { - if src.Status == Present { - switch v := dst.(type) { - case *rune: - runes := []rune(src.String) - if len(runes) == 1 { - *v = runes[0] - return nil - } - } - } - return (*Text)(src).AssignTo(dst) -} - -func (dst *BPChar) DecodeText(ci *ConnInfo, src []byte) error { - return (*Text)(dst).DecodeText(ci, src) -} - -func (dst *BPChar) DecodeBinary(ci *ConnInfo, src []byte) error { - return (*Text)(dst).DecodeBinary(ci, src) -} - -func (src *BPChar) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*Text)(src).EncodeText(ci, buf) -} - -func (src *BPChar) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*Text)(src).EncodeBinary(ci, buf) -} - -// Scan implements the database/sql Scanner interface. -func (dst *BPChar) Scan(src interface{}) error { - return (*Text)(dst).Scan(src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *BPChar) Value() (driver.Value, error) { - return (*Text)(src).Value() -} - -func (src *BPChar) MarshalJSON() ([]byte, error) { - return (*Text)(src).MarshalJSON() -} - -func (dst *BPChar) UnmarshalJSON(b []byte) error { - return (*Text)(dst).UnmarshalJSON(b) -} diff --git a/pgtype/bpchar_array.go b/pgtype/bpchar_array.go deleted file mode 100644 index 547b4e80..00000000 --- a/pgtype/bpchar_array.go +++ /dev/null @@ -1,301 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type BPCharArray struct { - Elements []BPChar - Dimensions []ArrayDimension - Status Status -} - -func (dst *BPCharArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = BPCharArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []string: - if value == nil { - *dst = BPCharArray{Status: Null} - } else if len(value) == 0 { - *dst = BPCharArray{Status: Present} - } else { - elements := make([]BPChar, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = BPCharArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to BPCharArray", value) - } - - return nil -} - -func (dst *BPCharArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *BPCharArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]string: - *v = make([]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *BPCharArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = BPCharArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []BPChar - - if len(uta.Elements) > 0 { - elements = make([]BPChar, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem BPChar - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = BPCharArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *BPCharArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = BPCharArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = BPCharArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]BPChar, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = BPCharArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *BPCharArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *BPCharArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("bpchar"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "bpchar") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *BPCharArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *BPCharArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/bpchar_array_test.go b/pgtype/bpchar_array_test.go deleted file mode 100644 index 820dfa5b..00000000 --- a/pgtype/bpchar_array_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package pgtype_test - -import ( - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestBPCharArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "char(8)[]", []interface{}{ - &pgtype.BPCharArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.BPCharArray{ - Elements: []pgtype.BPChar{ - pgtype.BPChar{String: "foo ", Status: pgtype.Present}, - pgtype.BPChar{Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.BPCharArray{Status: pgtype.Null}, - &pgtype.BPCharArray{ - Elements: []pgtype.BPChar{ - pgtype.BPChar{String: "bar ", Status: pgtype.Present}, - pgtype.BPChar{String: "NuLL ", Status: pgtype.Present}, - pgtype.BPChar{String: `wow"quz\`, Status: pgtype.Present}, - pgtype.BPChar{String: "1 ", Status: pgtype.Present}, - pgtype.BPChar{String: "1 ", Status: pgtype.Present}, - pgtype.BPChar{String: "null ", Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 3, LowerBound: 1}, - {Length: 2, LowerBound: 1}, - }, - Status: pgtype.Present, - }, - &pgtype.BPCharArray{ - Elements: []pgtype.BPChar{ - pgtype.BPChar{String: " bar ", Status: pgtype.Present}, - pgtype.BPChar{String: " baz ", Status: pgtype.Present}, - pgtype.BPChar{String: " quz ", Status: pgtype.Present}, - pgtype.BPChar{String: "foo ", Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} diff --git a/pgtype/bpchar_test.go b/pgtype/bpchar_test.go deleted file mode 100644 index e8981e52..00000000 --- a/pgtype/bpchar_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestChar3Transcode(t *testing.T) { - testutil.TestSuccessfulTranscodeEqFunc(t, "char(3)", []interface{}{ - &pgtype.BPChar{String: "a ", Status: pgtype.Present}, - &pgtype.BPChar{String: " a ", Status: pgtype.Present}, - &pgtype.BPChar{String: "å—¨ ", Status: pgtype.Present}, - &pgtype.BPChar{String: " ", Status: pgtype.Present}, - &pgtype.BPChar{Status: pgtype.Null}, - }, func(aa, bb interface{}) bool { - a := aa.(pgtype.BPChar) - b := bb.(pgtype.BPChar) - - return a.Status == b.Status && a.String == b.String - }) -} - -func TestBPCharAssignTo(t *testing.T) { - var ( - str string - run rune - ) - simpleTests := []struct { - src pgtype.BPChar - dst interface{} - expected interface{} - }{ - {src: pgtype.BPChar{String: "simple", Status: pgtype.Present}, dst: &str, expected: "simple"}, - {src: pgtype.BPChar{String: "å—¨", Status: pgtype.Present}, dst: &run, expected: 'å—¨'}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - -} diff --git a/pgtype/bytea.go b/pgtype/bytea.go deleted file mode 100644 index e6c28dc7..00000000 --- a/pgtype/bytea.go +++ /dev/null @@ -1,157 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/hex" - - errors "golang.org/x/xerrors" -) - -type Bytea struct { - Bytes []byte - Status Status -} - -func (dst *Bytea) Set(src interface{}) error { - if src == nil { - *dst = Bytea{Status: Null} - return nil - } - - switch value := src.(type) { - case []byte: - if value != nil { - *dst = Bytea{Bytes: value, Status: Present} - } else { - *dst = Bytea{Status: Null} - } - default: - if originalSrc, ok := underlyingBytesType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Bytea", value) - } - - return nil -} - -func (dst *Bytea) Get() interface{} { - switch dst.Status { - case Present: - return dst.Bytes - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Bytea) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *[]byte: - buf := make([]byte, len(src.Bytes)) - copy(buf, src.Bytes) - *v = buf - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -// DecodeText only supports the hex format. This has been the default since -// PostgreSQL 9.0. -func (dst *Bytea) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Bytea{Status: Null} - return nil - } - - if len(src) < 2 || src[0] != '\\' || src[1] != 'x' { - return errors.Errorf("invalid hex format") - } - - buf := make([]byte, (len(src)-2)/2) - _, err := hex.Decode(buf, src[2:]) - if err != nil { - return err - } - - *dst = Bytea{Bytes: buf, Status: Present} - return nil -} - -func (dst *Bytea) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Bytea{Status: Null} - return nil - } - - *dst = Bytea{Bytes: src, Status: Present} - return nil -} - -func (src *Bytea) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = append(buf, `\x`...) - buf = append(buf, hex.EncodeToString(src.Bytes)...) - return buf, nil -} - -func (src *Bytea) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, src.Bytes...), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Bytea) Scan(src interface{}) error { - if src == nil { - *dst = Bytea{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - buf := make([]byte, len(src)) - copy(buf, src) - *dst = Bytea{Bytes: buf, Status: Present} - return nil - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Bytea) Value() (driver.Value, error) { - switch src.Status { - case Present: - return src.Bytes, nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} diff --git a/pgtype/bytea_array.go b/pgtype/bytea_array.go deleted file mode 100644 index 369d6e08..00000000 --- a/pgtype/bytea_array.go +++ /dev/null @@ -1,301 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type ByteaArray struct { - Elements []Bytea - Dimensions []ArrayDimension - Status Status -} - -func (dst *ByteaArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = ByteaArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case [][]byte: - if value == nil { - *dst = ByteaArray{Status: Null} - } else if len(value) == 0 { - *dst = ByteaArray{Status: Present} - } else { - elements := make([]Bytea, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = ByteaArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to ByteaArray", value) - } - - return nil -} - -func (dst *ByteaArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *ByteaArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[][]byte: - *v = make([][]byte, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *ByteaArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = ByteaArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Bytea - - if len(uta.Elements) > 0 { - elements = make([]Bytea, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Bytea - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = ByteaArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *ByteaArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = ByteaArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = ByteaArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Bytea, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = ByteaArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *ByteaArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *ByteaArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("bytea"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "bytea") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *ByteaArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *ByteaArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/bytea_array_test.go b/pgtype/bytea_array_test.go deleted file mode 100644 index 00dc0a1f..00000000 --- a/pgtype/bytea_array_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestByteaArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "bytea[]", []interface{}{ - &pgtype.ByteaArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.ByteaArray{ - Elements: []pgtype.Bytea{ - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.ByteaArray{Status: pgtype.Null}, - &pgtype.ByteaArray{ - Elements: []pgtype.Bytea{ - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Bytes: []byte{}, Status: pgtype.Present}, - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Bytes: []byte{1}, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.ByteaArray{ - Elements: []pgtype.Bytea{ - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Bytes: []byte{}, Status: pgtype.Present}, - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Bytes: []byte{1}, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestByteaArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.ByteaArray - }{ - { - source: [][]byte{{1, 2, 3}}, - result: pgtype.ByteaArray{ - Elements: []pgtype.Bytea{{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([][]byte)(nil)), - result: pgtype.ByteaArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.ByteaArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestByteaArrayAssignTo(t *testing.T) { - var byteByteSlice [][]byte - - simpleTests := []struct { - src pgtype.ByteaArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.ByteaArray{ - Elements: []pgtype.Bytea{{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &byteByteSlice, - expected: [][]byte{{1, 2, 3}}, - }, - { - src: pgtype.ByteaArray{Status: pgtype.Null}, - dst: &byteByteSlice, - expected: (([][]byte)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } -} diff --git a/pgtype/bytea_test.go b/pgtype/bytea_test.go deleted file mode 100644 index 75b55de4..00000000 --- a/pgtype/bytea_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestByteaTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "bytea", []interface{}{ - &pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - &pgtype.Bytea{Bytes: []byte{}, Status: pgtype.Present}, - &pgtype.Bytea{Bytes: nil, Status: pgtype.Null}, - }) -} - -func TestByteaSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Bytea - }{ - {source: []byte{1, 2, 3}, result: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}}, - {source: []byte{}, result: pgtype.Bytea{Bytes: []byte{}, Status: pgtype.Present}}, - {source: []byte(nil), result: pgtype.Bytea{Status: pgtype.Null}}, - {source: _byteSlice{1, 2, 3}, result: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}}, - {source: _byteSlice(nil), result: pgtype.Bytea{Status: pgtype.Null}}, - } - - for i, tt := range successfulTests { - var r pgtype.Bytea - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestByteaAssignTo(t *testing.T) { - var buf []byte - var _buf _byteSlice - var pbuf *[]byte - var _pbuf *_byteSlice - - simpleTests := []struct { - src pgtype.Bytea - dst interface{} - expected interface{} - }{ - {src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, dst: &buf, expected: []byte{1, 2, 3}}, - {src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, dst: &_buf, expected: _byteSlice{1, 2, 3}}, - {src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, dst: &pbuf, expected: &[]byte{1, 2, 3}}, - {src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, dst: &_pbuf, expected: &_byteSlice{1, 2, 3}}, - {src: pgtype.Bytea{Status: pgtype.Null}, dst: &pbuf, expected: ((*[]byte)(nil))}, - {src: pgtype.Bytea{Status: pgtype.Null}, dst: &_pbuf, expected: ((*_byteSlice)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } -} diff --git a/pgtype/cid.go b/pgtype/cid.go deleted file mode 100644 index 0ed54f44..00000000 --- a/pgtype/cid.go +++ /dev/null @@ -1,61 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" -) - -// CID is PostgreSQL's Command Identifier type. -// -// When one does -// -// select cmin, cmax, * from some_table; -// -// it is the data type of the cmin and cmax hidden system columns. -// -// It is currently implemented as an unsigned four byte integer. -// Its definition can be found in src/include/c.h as CommandId -// in the PostgreSQL sources. -type CID pguint32 - -// Set converts from src to dst. Note that as CID is not a general -// number type Set does not do automatic type conversion as other number -// types do. -func (dst *CID) Set(src interface{}) error { - return (*pguint32)(dst).Set(src) -} - -func (dst *CID) Get() interface{} { - return (*pguint32)(dst).Get() -} - -// AssignTo assigns from src to dst. Note that as CID is not a general number -// type AssignTo does not do automatic type conversion as other number types do. -func (src *CID) AssignTo(dst interface{}) error { - return (*pguint32)(src).AssignTo(dst) -} - -func (dst *CID) DecodeText(ci *ConnInfo, src []byte) error { - return (*pguint32)(dst).DecodeText(ci, src) -} - -func (dst *CID) DecodeBinary(ci *ConnInfo, src []byte) error { - return (*pguint32)(dst).DecodeBinary(ci, src) -} - -func (src *CID) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*pguint32)(src).EncodeText(ci, buf) -} - -func (src *CID) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*pguint32)(src).EncodeBinary(ci, buf) -} - -// Scan implements the database/sql Scanner interface. -func (dst *CID) Scan(src interface{}) error { - return (*pguint32)(dst).Scan(src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *CID) Value() (driver.Value, error) { - return (*pguint32)(src).Value() -} diff --git a/pgtype/cid_test.go b/pgtype/cid_test.go deleted file mode 100644 index 588e6c66..00000000 --- a/pgtype/cid_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestCIDTranscode(t *testing.T) { - pgTypeName := "cid" - values := []interface{}{ - &pgtype.CID{Uint: 42, Status: pgtype.Present}, - &pgtype.CID{Status: pgtype.Null}, - } - eqFunc := func(a, b interface{}) bool { - return reflect.DeepEqual(a, b) - } - - testutil.TestPgxSuccessfulTranscodeEqFunc(t, pgTypeName, values, eqFunc) - - for _, driverName := range []string{"github.com/lib/pq", "github.com/jackc/pgx/stdlib"} { - testutil.TestDatabaseSQLSuccessfulTranscodeEqFunc(t, driverName, pgTypeName, values, eqFunc) - } -} - -func TestCIDSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.CID - }{ - {source: uint32(1), result: pgtype.CID{Uint: 1, Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var r pgtype.CID - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestCIDAssignTo(t *testing.T) { - var ui32 uint32 - var pui32 *uint32 - - simpleTests := []struct { - src pgtype.CID - dst interface{} - expected interface{} - }{ - {src: pgtype.CID{Uint: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.CID{Status: pgtype.Null}, dst: &pui32, expected: ((*uint32)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.CID - dst interface{} - expected interface{} - }{ - {src: pgtype.CID{Uint: 42, Status: pgtype.Present}, dst: &pui32, expected: uint32(42)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.CID - dst interface{} - }{ - {src: pgtype.CID{Status: pgtype.Null}, dst: &ui32}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/cidr.go b/pgtype/cidr.go deleted file mode 100644 index 519b9cae..00000000 --- a/pgtype/cidr.go +++ /dev/null @@ -1,31 +0,0 @@ -package pgtype - -type CIDR Inet - -func (dst *CIDR) Set(src interface{}) error { - return (*Inet)(dst).Set(src) -} - -func (dst *CIDR) Get() interface{} { - return (*Inet)(dst).Get() -} - -func (src *CIDR) AssignTo(dst interface{}) error { - return (*Inet)(src).AssignTo(dst) -} - -func (dst *CIDR) DecodeText(ci *ConnInfo, src []byte) error { - return (*Inet)(dst).DecodeText(ci, src) -} - -func (dst *CIDR) DecodeBinary(ci *ConnInfo, src []byte) error { - return (*Inet)(dst).DecodeBinary(ci, src) -} - -func (src *CIDR) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*Inet)(src).EncodeText(ci, buf) -} - -func (src *CIDR) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*Inet)(src).EncodeBinary(ci, buf) -} diff --git a/pgtype/cidr_array.go b/pgtype/cidr_array.go deleted file mode 100644 index 94c07679..00000000 --- a/pgtype/cidr_array.go +++ /dev/null @@ -1,330 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "net" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type CIDRArray struct { - Elements []CIDR - Dimensions []ArrayDimension - Status Status -} - -func (dst *CIDRArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = CIDRArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []*net.IPNet: - if value == nil { - *dst = CIDRArray{Status: Null} - } else if len(value) == 0 { - *dst = CIDRArray{Status: Present} - } else { - elements := make([]CIDR, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = CIDRArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - case []net.IP: - if value == nil { - *dst = CIDRArray{Status: Null} - } else if len(value) == 0 { - *dst = CIDRArray{Status: Present} - } else { - elements := make([]CIDR, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = CIDRArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to CIDRArray", value) - } - - return nil -} - -func (dst *CIDRArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *CIDRArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]*net.IPNet: - *v = make([]*net.IPNet, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]net.IP: - *v = make([]net.IP, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *CIDRArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = CIDRArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []CIDR - - if len(uta.Elements) > 0 { - elements = make([]CIDR, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem CIDR - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = CIDRArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *CIDRArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = CIDRArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = CIDRArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]CIDR, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = CIDRArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *CIDRArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *CIDRArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("cidr"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "cidr") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *CIDRArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *CIDRArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/cidr_array_test.go b/pgtype/cidr_array_test.go deleted file mode 100644 index 71125bdb..00000000 --- a/pgtype/cidr_array_test.go +++ /dev/null @@ -1,165 +0,0 @@ -package pgtype_test - -import ( - "net" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestCIDRArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "cidr[]", []interface{}{ - &pgtype.CIDRArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.CIDRArray{ - Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.CIDRArray{Status: pgtype.Null}, - &pgtype.CIDRArray{ - Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present}, - {Status: pgtype.Null}, - {IPNet: mustParseCIDR(t, "255.0.0.0/8"), Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.CIDRArray{ - Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestCIDRArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.CIDRArray - }{ - { - source: []*net.IPNet{mustParseCIDR(t, "127.0.0.1/32")}, - result: pgtype.CIDRArray{ - Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]*net.IPNet)(nil)), - result: pgtype.CIDRArray{Status: pgtype.Null}, - }, - { - source: []net.IP{mustParseCIDR(t, "127.0.0.1/32").IP}, - result: pgtype.CIDRArray{ - Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]net.IP)(nil)), - result: pgtype.CIDRArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.CIDRArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestCIDRArrayAssignTo(t *testing.T) { - var ipnetSlice []*net.IPNet - var ipSlice []net.IP - - simpleTests := []struct { - src pgtype.CIDRArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.CIDRArray{ - Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &ipnetSlice, - expected: []*net.IPNet{mustParseCIDR(t, "127.0.0.1/32")}, - }, - { - src: pgtype.CIDRArray{ - Elements: []pgtype.CIDR{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &ipnetSlice, - expected: []*net.IPNet{nil}, - }, - { - src: pgtype.CIDRArray{ - Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &ipSlice, - expected: []net.IP{mustParseCIDR(t, "127.0.0.1/32").IP}, - }, - { - src: pgtype.CIDRArray{ - Elements: []pgtype.CIDR{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &ipSlice, - expected: []net.IP{nil}, - }, - { - src: pgtype.CIDRArray{Status: pgtype.Null}, - dst: &ipnetSlice, - expected: (([]*net.IPNet)(nil)), - }, - { - src: pgtype.CIDRArray{Status: pgtype.Null}, - dst: &ipSlice, - expected: (([]net.IP)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } -} diff --git a/pgtype/circle.go b/pgtype/circle.go deleted file mode 100644 index 66dec132..00000000 --- a/pgtype/circle.go +++ /dev/null @@ -1,151 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "fmt" - "math" - "strconv" - "strings" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Circle struct { - P Vec2 - R float64 - Status Status -} - -func (dst *Circle) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Circle", src) -} - -func (dst *Circle) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Circle) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Circle) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Circle{Status: Null} - return nil - } - - if len(src) < 9 { - return errors.Errorf("invalid length for Circle: %v", len(src)) - } - - str := string(src[2:]) - end := strings.IndexByte(str, ',') - x, err := strconv.ParseFloat(str[:end], 64) - if err != nil { - return err - } - - str = str[end+1:] - end = strings.IndexByte(str, ')') - - y, err := strconv.ParseFloat(str[:end], 64) - if err != nil { - return err - } - - str = str[end+2 : len(str)-1] - - r, err := strconv.ParseFloat(str, 64) - if err != nil { - return err - } - - *dst = Circle{P: Vec2{x, y}, R: r, Status: Present} - return nil -} - -func (dst *Circle) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Circle{Status: Null} - return nil - } - - if len(src) != 24 { - return errors.Errorf("invalid length for Circle: %v", len(src)) - } - - x := binary.BigEndian.Uint64(src) - y := binary.BigEndian.Uint64(src[8:]) - r := binary.BigEndian.Uint64(src[16:]) - - *dst = Circle{ - P: Vec2{math.Float64frombits(x), math.Float64frombits(y)}, - R: math.Float64frombits(r), - Status: Present, - } - return nil -} - -func (src *Circle) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = append(buf, fmt.Sprintf(`<(%s,%s),%s>`, - strconv.FormatFloat(src.P.X, 'f', -1, 64), - strconv.FormatFloat(src.P.Y, 'f', -1, 64), - strconv.FormatFloat(src.R, 'f', -1, 64), - )...) - - return buf, nil -} - -func (src *Circle) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = pgio.AppendUint64(buf, math.Float64bits(src.P.X)) - buf = pgio.AppendUint64(buf, math.Float64bits(src.P.Y)) - buf = pgio.AppendUint64(buf, math.Float64bits(src.R)) - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Circle) Scan(src interface{}) error { - if src == nil { - *dst = Circle{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Circle) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/circle_test.go b/pgtype/circle_test.go deleted file mode 100644 index 82598620..00000000 --- a/pgtype/circle_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package pgtype_test - -import ( - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestCircleTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "circle", []interface{}{ - &pgtype.Circle{P: pgtype.Vec2{1.234, 5.67890123}, R: 3.5, Status: pgtype.Present}, - &pgtype.Circle{P: pgtype.Vec2{-1.234, -5.6789}, R: 12.9, Status: pgtype.Present}, - &pgtype.Circle{Status: pgtype.Null}, - }) -} diff --git a/pgtype/convert.go b/pgtype/convert.go deleted file mode 100644 index 98999d45..00000000 --- a/pgtype/convert.go +++ /dev/null @@ -1,424 +0,0 @@ -package pgtype - -import ( - "math" - "reflect" - "time" - - errors "golang.org/x/xerrors" -) - -const maxUint = ^uint(0) -const maxInt = int(maxUint >> 1) -const minInt = -maxInt - 1 - -// underlyingNumberType gets the underlying type that can be converted to Int2, Int4, Int8, Float4, or Float8 -func underlyingNumberType(val interface{}) (interface{}, bool) { - refVal := reflect.ValueOf(val) - - switch refVal.Kind() { - case reflect.Ptr: - if refVal.IsNil() { - return nil, false - } - convVal := refVal.Elem().Interface() - return convVal, true - case reflect.Int: - convVal := int(refVal.Int()) - return convVal, reflect.TypeOf(convVal) != refVal.Type() - case reflect.Int8: - convVal := int8(refVal.Int()) - return convVal, reflect.TypeOf(convVal) != refVal.Type() - case reflect.Int16: - convVal := int16(refVal.Int()) - return convVal, reflect.TypeOf(convVal) != refVal.Type() - case reflect.Int32: - convVal := int32(refVal.Int()) - return convVal, reflect.TypeOf(convVal) != refVal.Type() - case reflect.Int64: - convVal := int64(refVal.Int()) - return convVal, reflect.TypeOf(convVal) != refVal.Type() - case reflect.Uint: - convVal := uint(refVal.Uint()) - return convVal, reflect.TypeOf(convVal) != refVal.Type() - case reflect.Uint8: - convVal := uint8(refVal.Uint()) - return convVal, reflect.TypeOf(convVal) != refVal.Type() - case reflect.Uint16: - convVal := uint16(refVal.Uint()) - return convVal, reflect.TypeOf(convVal) != refVal.Type() - case reflect.Uint32: - convVal := uint32(refVal.Uint()) - return convVal, reflect.TypeOf(convVal) != refVal.Type() - case reflect.Uint64: - convVal := uint64(refVal.Uint()) - return convVal, reflect.TypeOf(convVal) != refVal.Type() - case reflect.Float32: - convVal := float32(refVal.Float()) - return convVal, reflect.TypeOf(convVal) != refVal.Type() - case reflect.Float64: - convVal := refVal.Float() - return convVal, reflect.TypeOf(convVal) != refVal.Type() - case reflect.String: - convVal := refVal.String() - return convVal, reflect.TypeOf(convVal) != refVal.Type() - } - - return nil, false -} - -// underlyingBoolType gets the underlying type that can be converted to Bool -func underlyingBoolType(val interface{}) (interface{}, bool) { - refVal := reflect.ValueOf(val) - - switch refVal.Kind() { - case reflect.Ptr: - if refVal.IsNil() { - return nil, false - } - convVal := refVal.Elem().Interface() - return convVal, true - case reflect.Bool: - convVal := refVal.Bool() - return convVal, reflect.TypeOf(convVal) != refVal.Type() - } - - return nil, false -} - -// underlyingBytesType gets the underlying type that can be converted to []byte -func underlyingBytesType(val interface{}) (interface{}, bool) { - refVal := reflect.ValueOf(val) - - switch refVal.Kind() { - case reflect.Ptr: - if refVal.IsNil() { - return nil, false - } - convVal := refVal.Elem().Interface() - return convVal, true - case reflect.Slice: - if refVal.Type().Elem().Kind() == reflect.Uint8 { - convVal := refVal.Bytes() - return convVal, reflect.TypeOf(convVal) != refVal.Type() - } - } - - return nil, false -} - -// underlyingStringType gets the underlying type that can be converted to String -func underlyingStringType(val interface{}) (interface{}, bool) { - refVal := reflect.ValueOf(val) - - switch refVal.Kind() { - case reflect.Ptr: - if refVal.IsNil() { - return nil, false - } - convVal := refVal.Elem().Interface() - return convVal, true - case reflect.String: - convVal := refVal.String() - return convVal, reflect.TypeOf(convVal) != refVal.Type() - } - - return nil, false -} - -// underlyingPtrType dereferences a pointer -func underlyingPtrType(val interface{}) (interface{}, bool) { - refVal := reflect.ValueOf(val) - - switch refVal.Kind() { - case reflect.Ptr: - if refVal.IsNil() { - return nil, false - } - convVal := refVal.Elem().Interface() - return convVal, true - } - - return nil, false -} - -// underlyingTimeType gets the underlying type that can be converted to time.Time -func underlyingTimeType(val interface{}) (interface{}, bool) { - refVal := reflect.ValueOf(val) - - switch refVal.Kind() { - case reflect.Ptr: - if refVal.IsNil() { - return time.Time{}, false - } - convVal := refVal.Elem().Interface() - return convVal, true - } - - timeType := reflect.TypeOf(time.Time{}) - if refVal.Type().ConvertibleTo(timeType) { - return refVal.Convert(timeType).Interface(), true - } - - return time.Time{}, false -} - -// underlyingSliceType gets the underlying slice type -func underlyingSliceType(val interface{}) (interface{}, bool) { - refVal := reflect.ValueOf(val) - - switch refVal.Kind() { - case reflect.Ptr: - if refVal.IsNil() { - return nil, false - } - convVal := refVal.Elem().Interface() - return convVal, true - case reflect.Slice: - baseSliceType := reflect.SliceOf(refVal.Type().Elem()) - if refVal.Type().ConvertibleTo(baseSliceType) { - convVal := refVal.Convert(baseSliceType) - return convVal.Interface(), reflect.TypeOf(convVal.Interface()) != refVal.Type() - } - } - - return nil, false -} - -func int64AssignTo(srcVal int64, srcStatus Status, dst interface{}) error { - if srcStatus == Present { - switch v := dst.(type) { - case *int: - if srcVal < int64(minInt) { - return errors.Errorf("%d is less than minimum value for int", srcVal) - } else if srcVal > int64(maxInt) { - return errors.Errorf("%d is greater than maximum value for int", srcVal) - } - *v = int(srcVal) - case *int8: - if srcVal < math.MinInt8 { - return errors.Errorf("%d is less than minimum value for int8", srcVal) - } else if srcVal > math.MaxInt8 { - return errors.Errorf("%d is greater than maximum value for int8", srcVal) - } - *v = int8(srcVal) - case *int16: - if srcVal < math.MinInt16 { - return errors.Errorf("%d is less than minimum value for int16", srcVal) - } else if srcVal > math.MaxInt16 { - return errors.Errorf("%d is greater than maximum value for int16", srcVal) - } - *v = int16(srcVal) - case *int32: - if srcVal < math.MinInt32 { - return errors.Errorf("%d is less than minimum value for int32", srcVal) - } else if srcVal > math.MaxInt32 { - return errors.Errorf("%d is greater than maximum value for int32", srcVal) - } - *v = int32(srcVal) - case *int64: - if srcVal < math.MinInt64 { - return errors.Errorf("%d is less than minimum value for int64", srcVal) - } else if srcVal > math.MaxInt64 { - return errors.Errorf("%d is greater than maximum value for int64", srcVal) - } - *v = int64(srcVal) - case *uint: - if srcVal < 0 { - return errors.Errorf("%d is less than zero for uint", srcVal) - } else if uint64(srcVal) > uint64(maxUint) { - return errors.Errorf("%d is greater than maximum value for uint", srcVal) - } - *v = uint(srcVal) - case *uint8: - if srcVal < 0 { - return errors.Errorf("%d is less than zero for uint8", srcVal) - } else if srcVal > math.MaxUint8 { - return errors.Errorf("%d is greater than maximum value for uint8", srcVal) - } - *v = uint8(srcVal) - case *uint16: - if srcVal < 0 { - return errors.Errorf("%d is less than zero for uint32", srcVal) - } else if srcVal > math.MaxUint16 { - return errors.Errorf("%d is greater than maximum value for uint16", srcVal) - } - *v = uint16(srcVal) - case *uint32: - if srcVal < 0 { - return errors.Errorf("%d is less than zero for uint32", srcVal) - } else if srcVal > math.MaxUint32 { - return errors.Errorf("%d is greater than maximum value for uint32", srcVal) - } - *v = uint32(srcVal) - case *uint64: - if srcVal < 0 { - return errors.Errorf("%d is less than zero for uint64", srcVal) - } - *v = uint64(srcVal) - default: - if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr { - el := v.Elem() - switch el.Kind() { - // if dst is a pointer to pointer, strip the pointer and try again - case reflect.Ptr: - if el.IsNil() { - // allocate destination - el.Set(reflect.New(el.Type().Elem())) - } - return int64AssignTo(srcVal, srcStatus, el.Interface()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if el.OverflowInt(int64(srcVal)) { - return errors.Errorf("cannot put %d into %T", srcVal, dst) - } - el.SetInt(int64(srcVal)) - return nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - if srcVal < 0 { - return errors.Errorf("%d is less than zero for %T", srcVal, dst) - } - if el.OverflowUint(uint64(srcVal)) { - return errors.Errorf("cannot put %d into %T", srcVal, dst) - } - el.SetUint(uint64(srcVal)) - return nil - } - } - return errors.Errorf("cannot assign %v into %T", srcVal, dst) - } - return nil - } - - // if dst is a pointer to pointer and srcStatus is not Present, nil it out - if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr { - el := v.Elem() - if el.Kind() == reflect.Ptr { - el.Set(reflect.Zero(el.Type())) - return nil - } - } - - return errors.Errorf("cannot assign %v %v into %T", srcVal, srcStatus, dst) -} - -func float64AssignTo(srcVal float64, srcStatus Status, dst interface{}) error { - if srcStatus == Present { - switch v := dst.(type) { - case *float32: - *v = float32(srcVal) - case *float64: - *v = srcVal - default: - if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr { - el := v.Elem() - switch el.Kind() { - // if dst is a pointer to pointer, strip the pointer and try again - case reflect.Ptr: - if el.IsNil() { - // allocate destination - el.Set(reflect.New(el.Type().Elem())) - } - return float64AssignTo(srcVal, srcStatus, el.Interface()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - i64 := int64(srcVal) - if float64(i64) == srcVal { - return int64AssignTo(i64, srcStatus, dst) - } - } - } - return errors.Errorf("cannot assign %v into %T", srcVal, dst) - } - return nil - } - - // if dst is a pointer to pointer and srcStatus is not Present, nil it out - if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr { - el := v.Elem() - if el.Kind() == reflect.Ptr { - el.Set(reflect.Zero(el.Type())) - return nil - } - } - - return errors.Errorf("cannot assign %v %v into %T", srcVal, srcStatus, dst) -} - -func NullAssignTo(dst interface{}) error { - dstPtr := reflect.ValueOf(dst) - - // AssignTo dst must always be a pointer - if dstPtr.Kind() != reflect.Ptr { - return errors.Errorf("cannot assign NULL to %T", dst) - } - - dstVal := dstPtr.Elem() - - switch dstVal.Kind() { - case reflect.Ptr, reflect.Slice, reflect.Map: - dstVal.Set(reflect.Zero(dstVal.Type())) - return nil - } - - return errors.Errorf("cannot assign NULL to %T", dst) -} - -var kindTypes map[reflect.Kind]reflect.Type - -// GetAssignToDstType attempts to convert dst to something AssignTo can assign -// to. If dst is a pointer to pointer it allocates a value and returns the -// dereferences pointer. If dst is a named type such as *Foo where Foo is type -// Foo int16, it converts dst to *int16. -// -// GetAssignToDstType returns the converted dst and a bool representing if any -// change was made. -func GetAssignToDstType(dst interface{}) (interface{}, bool) { - dstPtr := reflect.ValueOf(dst) - - // AssignTo dst must always be a pointer - if dstPtr.Kind() != reflect.Ptr { - return nil, false - } - - dstVal := dstPtr.Elem() - - // if dst is a pointer to pointer, allocate space try again with the dereferenced pointer - if dstVal.Kind() == reflect.Ptr { - dstVal.Set(reflect.New(dstVal.Type().Elem())) - return dstVal.Interface(), true - } - - // if dst is pointer to a base type that has been renamed - if baseValType, ok := kindTypes[dstVal.Kind()]; ok { - nextDst := dstPtr.Convert(reflect.PtrTo(baseValType)) - return nextDst.Interface(), dstPtr.Type() != nextDst.Type() - } - - if dstVal.Kind() == reflect.Slice { - if baseElemType, ok := kindTypes[dstVal.Type().Elem().Kind()]; ok { - baseSliceType := reflect.PtrTo(reflect.SliceOf(baseElemType)) - nextDst := dstPtr.Convert(baseSliceType) - return nextDst.Interface(), dstPtr.Type() != nextDst.Type() - } - } - - return nil, false -} - -func init() { - kindTypes = map[reflect.Kind]reflect.Type{ - reflect.Bool: reflect.TypeOf(false), - reflect.Float32: reflect.TypeOf(float32(0)), - reflect.Float64: reflect.TypeOf(float64(0)), - reflect.Int: reflect.TypeOf(int(0)), - reflect.Int8: reflect.TypeOf(int8(0)), - reflect.Int16: reflect.TypeOf(int16(0)), - reflect.Int32: reflect.TypeOf(int32(0)), - reflect.Int64: reflect.TypeOf(int64(0)), - reflect.Uint: reflect.TypeOf(uint(0)), - reflect.Uint8: reflect.TypeOf(uint8(0)), - reflect.Uint16: reflect.TypeOf(uint16(0)), - reflect.Uint32: reflect.TypeOf(uint32(0)), - reflect.Uint64: reflect.TypeOf(uint64(0)), - reflect.String: reflect.TypeOf(""), - } -} diff --git a/pgtype/database_sql.go b/pgtype/database_sql.go deleted file mode 100644 index f54a750d..00000000 --- a/pgtype/database_sql.go +++ /dev/null @@ -1,42 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - - errors "golang.org/x/xerrors" -) - -func DatabaseSQLValue(ci *ConnInfo, src Value) (interface{}, error) { - if valuer, ok := src.(driver.Valuer); ok { - return valuer.Value() - } - - if textEncoder, ok := src.(TextEncoder); ok { - buf, err := textEncoder.EncodeText(ci, nil) - if err != nil { - return nil, err - } - return string(buf), nil - } - - if binaryEncoder, ok := src.(BinaryEncoder); ok { - buf, err := binaryEncoder.EncodeBinary(ci, nil) - if err != nil { - return nil, err - } - return buf, nil - } - - return nil, errors.New("cannot convert to database/sql compatible value") -} - -func EncodeValueText(src TextEncoder) (interface{}, error) { - buf, err := src.EncodeText(nil, make([]byte, 0, 32)) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - return string(buf), err -} diff --git a/pgtype/date.go b/pgtype/date.go deleted file mode 100644 index 08ba8c08..00000000 --- a/pgtype/date.go +++ /dev/null @@ -1,210 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "time" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Date struct { - Time time.Time - Status Status - InfinityModifier InfinityModifier -} - -const ( - negativeInfinityDayOffset = -2147483648 - infinityDayOffset = 2147483647 -) - -func (dst *Date) Set(src interface{}) error { - if src == nil { - *dst = Date{Status: Null} - return nil - } - - switch value := src.(type) { - case time.Time: - *dst = Date{Time: value, Status: Present} - default: - if originalSrc, ok := underlyingTimeType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Date", value) - } - - return nil -} - -func (dst *Date) Get() interface{} { - switch dst.Status { - case Present: - if dst.InfinityModifier != None { - return dst.InfinityModifier - } - return dst.Time - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Date) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *time.Time: - if src.InfinityModifier != None { - return errors.Errorf("cannot assign %v to %T", src, dst) - } - *v = src.Time - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Date) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Date{Status: Null} - return nil - } - - sbuf := string(src) - switch sbuf { - case "infinity": - *dst = Date{Status: Present, InfinityModifier: Infinity} - case "-infinity": - *dst = Date{Status: Present, InfinityModifier: -Infinity} - default: - t, err := time.ParseInLocation("2006-01-02", sbuf, time.UTC) - if err != nil { - return err - } - - *dst = Date{Time: t, Status: Present} - } - - return nil -} - -func (dst *Date) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Date{Status: Null} - return nil - } - - if len(src) != 4 { - return errors.Errorf("invalid length for date: %v", len(src)) - } - - dayOffset := int32(binary.BigEndian.Uint32(src)) - - switch dayOffset { - case infinityDayOffset: - *dst = Date{Status: Present, InfinityModifier: Infinity} - case negativeInfinityDayOffset: - *dst = Date{Status: Present, InfinityModifier: -Infinity} - default: - t := time.Date(2000, 1, int(1+dayOffset), 0, 0, 0, 0, time.UTC) - *dst = Date{Time: t, Status: Present} - } - - return nil -} - -func (src *Date) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var s string - - switch src.InfinityModifier { - case None: - s = src.Time.Format("2006-01-02") - case Infinity: - s = "infinity" - case NegativeInfinity: - s = "-infinity" - } - - return append(buf, s...), nil -} - -func (src *Date) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var daysSinceDateEpoch int32 - switch src.InfinityModifier { - case None: - tUnix := time.Date(src.Time.Year(), src.Time.Month(), src.Time.Day(), 0, 0, 0, 0, time.UTC).Unix() - dateEpoch := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC).Unix() - - secSinceDateEpoch := tUnix - dateEpoch - daysSinceDateEpoch = int32(secSinceDateEpoch / 86400) - case Infinity: - daysSinceDateEpoch = infinityDayOffset - case NegativeInfinity: - daysSinceDateEpoch = negativeInfinityDayOffset - } - - return pgio.AppendInt32(buf, daysSinceDateEpoch), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Date) Scan(src interface{}) error { - if src == nil { - *dst = Date{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - case time.Time: - *dst = Date{Time: src, Status: Present} - return nil - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Date) Value() (driver.Value, error) { - switch src.Status { - case Present: - if src.InfinityModifier != None { - return src.InfinityModifier.String(), nil - } - return src.Time, nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} diff --git a/pgtype/date_array.go b/pgtype/date_array.go deleted file mode 100644 index 05070360..00000000 --- a/pgtype/date_array.go +++ /dev/null @@ -1,302 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "time" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type DateArray struct { - Elements []Date - Dimensions []ArrayDimension - Status Status -} - -func (dst *DateArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = DateArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []time.Time: - if value == nil { - *dst = DateArray{Status: Null} - } else if len(value) == 0 { - *dst = DateArray{Status: Present} - } else { - elements := make([]Date, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = DateArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to DateArray", value) - } - - return nil -} - -func (dst *DateArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *DateArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]time.Time: - *v = make([]time.Time, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *DateArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = DateArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Date - - if len(uta.Elements) > 0 { - elements = make([]Date, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Date - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = DateArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *DateArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = DateArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = DateArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Date, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = DateArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *DateArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *DateArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("date"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "date") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *DateArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *DateArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/date_array_test.go b/pgtype/date_array_test.go deleted file mode 100644 index 24a8282c..00000000 --- a/pgtype/date_array_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - "time" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestDateArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "date[]", []interface{}{ - &pgtype.DateArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.DateArray{ - Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.DateArray{Status: pgtype.Null}, - &pgtype.DateArray{ - Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Status: pgtype.Null}, - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.DateArray{ - Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 3, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestDateArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.DateArray - }{ - { - source: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, - result: pgtype.DateArray{ - Elements: []pgtype.Date{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]time.Time)(nil)), - result: pgtype.DateArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.DateArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestDateArrayAssignTo(t *testing.T) { - var timeSlice []time.Time - - simpleTests := []struct { - src pgtype.DateArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.DateArray{ - Elements: []pgtype.Date{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &timeSlice, - expected: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, - }, - { - src: pgtype.DateArray{Status: pgtype.Null}, - dst: &timeSlice, - expected: (([]time.Time)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.DateArray - dst interface{} - }{ - { - src: pgtype.DateArray{ - Elements: []pgtype.Date{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &timeSlice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } - -} diff --git a/pgtype/date_test.go b/pgtype/date_test.go deleted file mode 100644 index ac7aadfe..00000000 --- a/pgtype/date_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - "time" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestDateTranscode(t *testing.T) { - testutil.TestSuccessfulTranscodeEqFunc(t, "date", []interface{}{ - &pgtype.Date{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Date{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Date{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Date{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Date{Status: pgtype.Null}, - &pgtype.Date{Status: pgtype.Present, InfinityModifier: pgtype.Infinity}, - &pgtype.Date{Status: pgtype.Present, InfinityModifier: -pgtype.Infinity}, - }, func(a, b interface{}) bool { - at := a.(pgtype.Date) - bt := b.(pgtype.Date) - - return at.Time.Equal(bt.Time) && at.Status == bt.Status && at.InfinityModifier == bt.InfinityModifier - }) -} - -func TestDateSet(t *testing.T) { - type _time time.Time - - successfulTests := []struct { - source interface{} - result pgtype.Date - }{ - {source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)), result: pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var d pgtype.Date - err := d.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if d != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, d) - } - } -} - -func TestDateAssignTo(t *testing.T) { - var tim time.Time - var ptim *time.Time - - simpleTests := []struct { - src pgtype.Date - dst interface{} - expected interface{} - }{ - {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &tim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, - {src: pgtype.Date{Time: time.Time{}, Status: pgtype.Null}, dst: &ptim, expected: ((*time.Time)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.Date - dst interface{} - expected interface{} - }{ - {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &ptim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Date - dst interface{} - }{ - {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.Infinity, Status: pgtype.Present}, dst: &tim}, - {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}, dst: &tim}, - {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Null}, dst: &tim}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/daterange.go b/pgtype/daterange.go deleted file mode 100644 index 40997bd9..00000000 --- a/pgtype/daterange.go +++ /dev/null @@ -1,250 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Daterange struct { - Lower Date - Upper Date - LowerType BoundType - UpperType BoundType - Status Status -} - -func (dst *Daterange) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Daterange", src) -} - -func (dst *Daterange) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Daterange) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Daterange) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Daterange{Status: Null} - return nil - } - - utr, err := ParseUntypedTextRange(string(src)) - if err != nil { - return err - } - - *dst = Daterange{Status: Present} - - dst.LowerType = utr.LowerType - dst.UpperType = utr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeText(ci, []byte(utr.Lower)); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeText(ci, []byte(utr.Upper)); err != nil { - return err - } - } - - return nil -} - -func (dst *Daterange) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Daterange{Status: Null} - return nil - } - - ubr, err := ParseUntypedBinaryRange(src) - if err != nil { - return err - } - - *dst = Daterange{Status: Present} - - dst.LowerType = ubr.LowerType - dst.UpperType = ubr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeBinary(ci, ubr.Lower); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeBinary(ci, ubr.Upper); err != nil { - return err - } - } - - return nil -} - -func (src Daterange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - switch src.LowerType { - case Exclusive, Unbounded: - buf = append(buf, '(') - case Inclusive: - buf = append(buf, '[') - case Empty: - return append(buf, "empty"...), nil - default: - return nil, errors.Errorf("unknown lower bound type %v", src.LowerType) - } - - var err error - - if src.LowerType != Unbounded { - buf, err = src.Lower.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - } - - buf = append(buf, ',') - - if src.UpperType != Unbounded { - buf, err = src.Upper.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - } - - switch src.UpperType { - case Exclusive, Unbounded: - buf = append(buf, ')') - case Inclusive: - buf = append(buf, ']') - default: - return nil, errors.Errorf("unknown upper bound type %v", src.UpperType) - } - - return buf, nil -} - -func (src Daterange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var rangeType byte - switch src.LowerType { - case Inclusive: - rangeType |= lowerInclusiveMask - case Unbounded: - rangeType |= lowerUnboundedMask - case Exclusive: - case Empty: - return append(buf, emptyMask), nil - default: - return nil, errors.Errorf("unknown LowerType: %v", src.LowerType) - } - - switch src.UpperType { - case Inclusive: - rangeType |= upperInclusiveMask - case Unbounded: - rangeType |= upperUnboundedMask - case Exclusive: - default: - return nil, errors.Errorf("unknown UpperType: %v", src.UpperType) - } - - buf = append(buf, rangeType) - - var err error - - if src.LowerType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Lower.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - if src.UpperType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Upper.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Daterange) Scan(src interface{}) error { - if src == nil { - *dst = Daterange{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src Daterange) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/daterange_test.go b/pgtype/daterange_test.go deleted file mode 100644 index 4d3119ee..00000000 --- a/pgtype/daterange_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package pgtype_test - -import ( - "testing" - "time" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestDaterangeTranscode(t *testing.T) { - testutil.TestSuccessfulTranscodeEqFunc(t, "daterange", []interface{}{ - &pgtype.Daterange{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Status: pgtype.Present}, - &pgtype.Daterange{ - Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - LowerType: pgtype.Inclusive, - UpperType: pgtype.Exclusive, - Status: pgtype.Present, - }, - &pgtype.Daterange{ - Lower: pgtype.Date{Time: time.Date(1800, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - LowerType: pgtype.Inclusive, - UpperType: pgtype.Exclusive, - Status: pgtype.Present, - }, - &pgtype.Daterange{Status: pgtype.Null}, - }, func(aa, bb interface{}) bool { - a := aa.(pgtype.Daterange) - b := bb.(pgtype.Daterange) - - return a.Status == b.Status && - a.Lower.Time.Equal(b.Lower.Time) && - a.Lower.Status == b.Lower.Status && - a.Lower.InfinityModifier == b.Lower.InfinityModifier && - a.Upper.Time.Equal(b.Upper.Time) && - a.Upper.Status == b.Upper.Status && - a.Upper.InfinityModifier == b.Upper.InfinityModifier - }) -} - -func TestDaterangeNormalize(t *testing.T) { - testutil.TestSuccessfulNormalizeEqFunc(t, []testutil.NormalizeTest{ - { - SQL: "select daterange('2010-01-01', '2010-01-11', '(]')", - Value: pgtype.Daterange{ - Lower: pgtype.Date{Time: time.Date(2010, 1, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Date{Time: time.Date(2010, 1, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - LowerType: pgtype.Inclusive, - UpperType: pgtype.Exclusive, - Status: pgtype.Present, - }, - }, - }, func(aa, bb interface{}) bool { - a := aa.(pgtype.Daterange) - b := bb.(pgtype.Daterange) - - return a.Status == b.Status && - a.Lower.Time.Equal(b.Lower.Time) && - a.Lower.Status == b.Lower.Status && - a.Lower.InfinityModifier == b.Lower.InfinityModifier && - a.Upper.Time.Equal(b.Upper.Time) && - a.Upper.Status == b.Upper.Status && - a.Upper.InfinityModifier == b.Upper.InfinityModifier - }) -} diff --git a/pgtype/enum_array.go b/pgtype/enum_array.go deleted file mode 100644 index 504d513c..00000000 --- a/pgtype/enum_array.go +++ /dev/null @@ -1,213 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - - errors "golang.org/x/xerrors" -) - -type EnumArray struct { - Elements []GenericText - Dimensions []ArrayDimension - Status Status -} - -func (dst *EnumArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = EnumArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []string: - if value == nil { - *dst = EnumArray{Status: Null} - } else if len(value) == 0 { - *dst = EnumArray{Status: Present} - } else { - elements := make([]GenericText, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = EnumArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to EnumArray", value) - } - - return nil -} - -func (dst *EnumArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *EnumArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]string: - *v = make([]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *EnumArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = EnumArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []GenericText - - if len(uta.Elements) > 0 { - elements = make([]GenericText, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem GenericText - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = EnumArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (src *EnumArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *EnumArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *EnumArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/enum_array_test.go b/pgtype/enum_array_test.go deleted file mode 100644 index dbe09751..00000000 --- a/pgtype/enum_array_test.go +++ /dev/null @@ -1,151 +0,0 @@ -package pgtype_test - -import ( - "context" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestEnumArrayTranscode(t *testing.T) { - setupConn := testutil.MustConnectPgx(t) - defer testutil.MustCloseContext(t, setupConn) - - if _, err := setupConn.Exec(context.Background(), "drop type if exists color"); err != nil { - t.Fatal(err) - } - if _, err := setupConn.Exec(context.Background(), "create type color as enum ('red', 'green', 'blue')"); err != nil { - t.Fatal(err) - } - - testutil.TestSuccessfulTranscode(t, "color[]", []interface{}{ - &pgtype.EnumArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.EnumArray{ - Elements: []pgtype.GenericText{ - {String: "red", Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.EnumArray{Status: pgtype.Null}, - &pgtype.EnumArray{ - Elements: []pgtype.GenericText{ - {String: "red", Status: pgtype.Present}, - {String: "green", Status: pgtype.Present}, - {String: "blue", Status: pgtype.Present}, - {String: "red", Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestEnumArrayArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.EnumArray - }{ - { - source: []string{"foo"}, - result: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{String: "foo", Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]string)(nil)), - result: pgtype.EnumArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.EnumArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestEnumArrayArrayAssignTo(t *testing.T) { - var stringSlice []string - type _stringSlice []string - var namedStringSlice _stringSlice - - simpleTests := []struct { - src pgtype.EnumArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{String: "foo", Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &stringSlice, - expected: []string{"foo"}, - }, - { - src: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{String: "bar", Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &namedStringSlice, - expected: _stringSlice{"bar"}, - }, - { - src: pgtype.EnumArray{Status: pgtype.Null}, - dst: &stringSlice, - expected: (([]string)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.EnumArray - dst interface{} - }{ - { - src: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &stringSlice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/ext/satori-uuid/uuid.go b/pgtype/ext/satori-uuid/uuid.go deleted file mode 100644 index 2aebfc47..00000000 --- a/pgtype/ext/satori-uuid/uuid.go +++ /dev/null @@ -1,162 +0,0 @@ -package uuid - -import ( - "database/sql/driver" - - errors "golang.org/x/xerrors" - - "github.com/jackc/pgx/v4/pgtype" - uuid "github.com/satori/go.uuid" -) - -var errUndefined = errors.New("cannot encode status undefined") - -type UUID struct { - UUID uuid.UUID - Status pgtype.Status -} - -func (dst *UUID) Set(src interface{}) error { - switch value := src.(type) { - case uuid.UUID: - *dst = UUID{UUID: value, Status: pgtype.Present} - case [16]byte: - *dst = UUID{UUID: uuid.UUID(value), Status: pgtype.Present} - case []byte: - if len(value) != 16 { - return errors.Errorf("[]byte must be 16 bytes to convert to UUID: %d", len(value)) - } - *dst = UUID{Status: pgtype.Present} - copy(dst.UUID[:], value) - case string: - uuid, err := uuid.FromString(value) - if err != nil { - return err - } - *dst = UUID{UUID: uuid, Status: pgtype.Present} - default: - // If all else fails see if pgtype.UUID can handle it. If so, translate through that. - pgUUID := &pgtype.UUID{} - if err := pgUUID.Set(value); err != nil { - return errors.Errorf("cannot convert %v to UUID", value) - } - - *dst = UUID{UUID: uuid.UUID(pgUUID.Bytes), Status: pgUUID.Status} - } - - return nil -} - -func (dst *UUID) Get() interface{} { - switch dst.Status { - case pgtype.Present: - return dst.UUID - case pgtype.Null: - return nil - default: - return dst.Status - } -} - -func (src *UUID) AssignTo(dst interface{}) error { - switch src.Status { - case pgtype.Present: - switch v := dst.(type) { - case *uuid.UUID: - *v = src.UUID - case *[16]byte: - *v = [16]byte(src.UUID) - return nil - case *[]byte: - *v = make([]byte, 16) - copy(*v, src.UUID[:]) - return nil - case *string: - *v = src.UUID.String() - return nil - default: - if nextDst, retry := pgtype.GetAssignToDstType(v); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case pgtype.Null: - return pgtype.NullAssignTo(dst) - } - - return errors.Errorf("cannot assign %v into %T", src, dst) -} - -func (dst *UUID) DecodeText(ci *pgtype.ConnInfo, src []byte) error { - if src == nil { - *dst = UUID{Status: pgtype.Null} - return nil - } - - u, err := uuid.FromString(string(src)) - if err != nil { - return err - } - - *dst = UUID{UUID: u, Status: pgtype.Present} - return nil -} - -func (dst *UUID) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { - if src == nil { - *dst = UUID{Status: pgtype.Null} - return nil - } - - if len(src) != 16 { - return errors.Errorf("invalid length for UUID: %v", len(src)) - } - - *dst = UUID{Status: pgtype.Present} - copy(dst.UUID[:], src) - return nil -} - -func (src *UUID) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case pgtype.Null: - return nil, nil - case pgtype.Undefined: - return nil, errUndefined - } - - return append(buf, src.UUID.String()...), nil -} - -func (src *UUID) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case pgtype.Null: - return nil, nil - case pgtype.Undefined: - return nil, errUndefined - } - - return append(buf, src.UUID[:]...), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *UUID) Scan(src interface{}) error { - if src == nil { - *dst = UUID{Status: pgtype.Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - return dst.DecodeText(nil, src) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *UUID) Value() (driver.Value, error) { - return pgtype.EncodeValueText(src) -} diff --git a/pgtype/ext/satori-uuid/uuid_test.go b/pgtype/ext/satori-uuid/uuid_test.go deleted file mode 100644 index 7a770b84..00000000 --- a/pgtype/ext/satori-uuid/uuid_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package uuid_test - -import ( - "bytes" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - satori "github.com/jackc/pgx/v4/pgtype/ext/satori-uuid" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestUUIDTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "uuid", []interface{}{ - &satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - &satori.UUID{Status: pgtype.Null}, - }) -} - -func TestUUIDSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result satori.UUID - }{ - { - source: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - result: satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - }, - { - source: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - result: satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - }, - { - source: "00010203-0405-0607-0809-0a0b0c0d0e0f", - result: satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - }, - } - - for i, tt := range successfulTests { - var r satori.UUID - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestUUIDAssignTo(t *testing.T) { - { - src := satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} - var dst [16]byte - expected := [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} - - err := src.AssignTo(&dst) - if err != nil { - t.Error(err) - } - - if dst != expected { - t.Errorf("expected %v to assign %v, but result was %v", src, expected, dst) - } - } - - { - src := satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} - var dst []byte - expected := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} - - err := src.AssignTo(&dst) - if err != nil { - t.Error(err) - } - - if bytes.Compare(dst, expected) != 0 { - t.Errorf("expected %v to assign %v, but result was %v", src, expected, dst) - } - } - - { - src := satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} - var dst string - expected := "00010203-0405-0607-0809-0a0b0c0d0e0f" - - err := src.AssignTo(&dst) - if err != nil { - t.Error(err) - } - - if dst != expected { - t.Errorf("expected %v to assign %v, but result was %v", src, expected, dst) - } - } - -} diff --git a/pgtype/ext/shopspring-numeric/decimal.go b/pgtype/ext/shopspring-numeric/decimal.go deleted file mode 100644 index 54612db9..00000000 --- a/pgtype/ext/shopspring-numeric/decimal.go +++ /dev/null @@ -1,318 +0,0 @@ -package numeric - -import ( - "database/sql/driver" - "strconv" - - errors "golang.org/x/xerrors" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/shopspring/decimal" -) - -var errUndefined = errors.New("cannot encode status undefined") - -type Numeric struct { - Decimal decimal.Decimal - Status pgtype.Status -} - -func (dst *Numeric) Set(src interface{}) error { - if src == nil { - *dst = Numeric{Status: pgtype.Null} - return nil - } - - switch value := src.(type) { - case decimal.Decimal: - *dst = Numeric{Decimal: value, Status: pgtype.Present} - case float32: - *dst = Numeric{Decimal: decimal.NewFromFloat(float64(value)), Status: pgtype.Present} - case float64: - *dst = Numeric{Decimal: decimal.NewFromFloat(value), Status: pgtype.Present} - case int8: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} - case uint8: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} - case int16: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} - case uint16: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} - case int32: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} - case uint32: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} - case int64: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} - case uint64: - // uint64 could be greater than int64 so convert to string then to decimal - dec, err := decimal.NewFromString(strconv.FormatUint(value, 10)) - if err != nil { - return err - } - *dst = Numeric{Decimal: dec, Status: pgtype.Present} - case int: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} - case uint: - // uint could be greater than int64 so convert to string then to decimal - dec, err := decimal.NewFromString(strconv.FormatUint(uint64(value), 10)) - if err != nil { - return err - } - *dst = Numeric{Decimal: dec, Status: pgtype.Present} - case string: - dec, err := decimal.NewFromString(value) - if err != nil { - return err - } - *dst = Numeric{Decimal: dec, Status: pgtype.Present} - default: - // If all else fails see if pgtype.Numeric can handle it. If so, translate through that. - num := &pgtype.Numeric{} - if err := num.Set(value); err != nil { - return errors.Errorf("cannot convert %v to Numeric", value) - } - - buf, err := num.EncodeText(nil, nil) - if err != nil { - return errors.Errorf("cannot convert %v to Numeric", value) - } - - dec, err := decimal.NewFromString(string(buf)) - if err != nil { - return errors.Errorf("cannot convert %v to Numeric", value) - } - *dst = Numeric{Decimal: dec, Status: pgtype.Present} - } - - return nil -} - -func (dst *Numeric) Get() interface{} { - switch dst.Status { - case pgtype.Present: - return dst.Decimal - case pgtype.Null: - return nil - default: - return dst.Status - } -} - -func (src *Numeric) AssignTo(dst interface{}) error { - switch src.Status { - case pgtype.Present: - switch v := dst.(type) { - case *decimal.Decimal: - *v = src.Decimal - case *float32: - f, _ := src.Decimal.Float64() - *v = float32(f) - case *float64: - f, _ := src.Decimal.Float64() - *v = f - case *int: - if src.Decimal.Exponent() < 0 { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseInt(src.Decimal.String(), 10, strconv.IntSize) - if err != nil { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - *v = int(n) - case *int8: - if src.Decimal.Exponent() < 0 { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseInt(src.Decimal.String(), 10, 8) - if err != nil { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - *v = int8(n) - case *int16: - if src.Decimal.Exponent() < 0 { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseInt(src.Decimal.String(), 10, 16) - if err != nil { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - *v = int16(n) - case *int32: - if src.Decimal.Exponent() < 0 { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseInt(src.Decimal.String(), 10, 32) - if err != nil { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - *v = int32(n) - case *int64: - if src.Decimal.Exponent() < 0 { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseInt(src.Decimal.String(), 10, 64) - if err != nil { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - *v = int64(n) - case *uint: - if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseUint(src.Decimal.String(), 10, strconv.IntSize) - if err != nil { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - *v = uint(n) - case *uint8: - if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseUint(src.Decimal.String(), 10, 8) - if err != nil { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - *v = uint8(n) - case *uint16: - if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseUint(src.Decimal.String(), 10, 16) - if err != nil { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - *v = uint16(n) - case *uint32: - if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseUint(src.Decimal.String(), 10, 32) - if err != nil { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - *v = uint32(n) - case *uint64: - if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseUint(src.Decimal.String(), 10, 64) - if err != nil { - return errors.Errorf("cannot convert %v to %T", dst, *v) - } - *v = uint64(n) - default: - if nextDst, retry := pgtype.GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case pgtype.Null: - return pgtype.NullAssignTo(dst) - } - - return nil -} - -func (dst *Numeric) DecodeText(ci *pgtype.ConnInfo, src []byte) error { - if src == nil { - *dst = Numeric{Status: pgtype.Null} - return nil - } - - dec, err := decimal.NewFromString(string(src)) - if err != nil { - return err - } - - *dst = Numeric{Decimal: dec, Status: pgtype.Present} - return nil -} - -func (dst *Numeric) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { - if src == nil { - *dst = Numeric{Status: pgtype.Null} - return nil - } - - // For now at least, implement this in terms of pgtype.Numeric - - num := &pgtype.Numeric{} - if err := num.DecodeBinary(ci, src); err != nil { - return err - } - - buf, err := num.EncodeText(ci, nil) - if err != nil { - return err - } - - dec, err := decimal.NewFromString(string(buf)) - if err != nil { - return err - } - - *dst = Numeric{Decimal: dec, Status: pgtype.Present} - - return nil -} - -func (src *Numeric) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case pgtype.Null: - return nil, nil - case pgtype.Undefined: - return nil, errUndefined - } - - return append(buf, src.Decimal.String()...), nil -} - -func (src *Numeric) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case pgtype.Null: - return nil, nil - case pgtype.Undefined: - return nil, errUndefined - } - - // For now at least, implement this in terms of pgtype.Numeric - num := &pgtype.Numeric{} - if err := num.DecodeText(ci, []byte(src.Decimal.String())); err != nil { - return nil, err - } - - return num.EncodeBinary(ci, buf) -} - -// Scan implements the database/sql Scanner interface. -func (dst *Numeric) Scan(src interface{}) error { - if src == nil { - *dst = Numeric{Status: pgtype.Null} - return nil - } - - switch src := src.(type) { - case float64: - *dst = Numeric{Decimal: decimal.NewFromFloat(src), Status: pgtype.Present} - return nil - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - return dst.DecodeText(nil, src) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Numeric) Value() (driver.Value, error) { - switch src.Status { - case pgtype.Present: - return src.Decimal.Value() - case pgtype.Null: - return nil, nil - default: - return nil, errUndefined - } -} diff --git a/pgtype/ext/shopspring-numeric/decimal_test.go b/pgtype/ext/shopspring-numeric/decimal_test.go deleted file mode 100644 index 2af39e1d..00000000 --- a/pgtype/ext/shopspring-numeric/decimal_test.go +++ /dev/null @@ -1,286 +0,0 @@ -package numeric_test - -import ( - "fmt" - "math/big" - "math/rand" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - shopspring "github.com/jackc/pgx/v4/pgtype/ext/shopspring-numeric" - "github.com/jackc/pgx/v4/pgtype/testutil" - "github.com/shopspring/decimal" -) - -func mustParseDecimal(t *testing.T, src string) decimal.Decimal { - dec, err := decimal.NewFromString(src) - if err != nil { - t.Fatal(err) - } - return dec -} - -func TestNumericNormalize(t *testing.T) { - testutil.TestSuccessfulNormalizeEqFunc(t, []testutil.NormalizeTest{ - { - SQL: "select '0'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "0"), Status: pgtype.Present}, - }, - { - SQL: "select '1'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}, - }, - { - SQL: "select '10.00'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "10.00"), Status: pgtype.Present}, - }, - { - SQL: "select '1e-3'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.001"), Status: pgtype.Present}, - }, - { - SQL: "select '-1'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, - }, - { - SQL: "select '10000'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "10000"), Status: pgtype.Present}, - }, - { - SQL: "select '3.14'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "3.14"), Status: pgtype.Present}, - }, - { - SQL: "select '1.1'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1.1"), Status: pgtype.Present}, - }, - { - SQL: "select '100010001'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "100010001"), Status: pgtype.Present}, - }, - { - SQL: "select '100010001.0001'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "100010001.0001"), Status: pgtype.Present}, - }, - { - SQL: "select '4237234789234789289347892374324872138321894178943189043890124832108934.43219085471578891547854892438945012347981'::numeric", - Value: &shopspring.Numeric{ - Decimal: mustParseDecimal(t, "4237234789234789289347892374324872138321894178943189043890124832108934.43219085471578891547854892438945012347981"), - Status: pgtype.Present, - }, - }, - { - SQL: "select '0.8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234'::numeric", - Value: &shopspring.Numeric{ - Decimal: mustParseDecimal(t, "0.8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234"), - Status: pgtype.Present, - }, - }, - { - SQL: "select '0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123'::numeric", - Value: &shopspring.Numeric{ - Decimal: mustParseDecimal(t, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123"), - Status: pgtype.Present, - }, - }, - }, func(aa, bb interface{}) bool { - a := aa.(shopspring.Numeric) - b := bb.(shopspring.Numeric) - - return a.Status == b.Status && a.Decimal.Equal(b.Decimal) - }) -} - -func TestNumericTranscode(t *testing.T) { - testutil.TestSuccessfulTranscodeEqFunc(t, "numeric", []interface{}{ - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "100000"), Status: pgtype.Present}, - - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.1"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.01"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.001"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.0001"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00001"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.000001"), Status: pgtype.Present}, - - &shopspring.Numeric{Decimal: mustParseDecimal(t, "3.14"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00000123"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.000000123"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.0000000123"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00000000123"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001234567890123456789"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "4309132809320932980457137401234890237489238912983572189348951289375283573984571892758234678903467889512893489128589347891272139.8489235871258912789347891235879148795891238915678189467128957812395781238579189025891238901583915890128973578957912385798125789012378905238905471598123758923478294374327894237892234"), Status: pgtype.Present}, - &shopspring.Numeric{Status: pgtype.Null}, - }, func(aa, bb interface{}) bool { - a := aa.(shopspring.Numeric) - b := bb.(shopspring.Numeric) - - return a.Status == b.Status && a.Decimal.Equal(b.Decimal) - }) - -} - -func TestNumericTranscodeFuzz(t *testing.T) { - r := rand.New(rand.NewSource(0)) - max := &big.Int{} - max.SetString("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 10) - - values := make([]interface{}, 0, 2000) - for i := 0; i < 500; i++ { - num := fmt.Sprintf("%s.%s", (&big.Int{}).Rand(r, max).String(), (&big.Int{}).Rand(r, max).String()) - negNum := "-" + num - values = append(values, &shopspring.Numeric{Decimal: mustParseDecimal(t, num), Status: pgtype.Present}) - values = append(values, &shopspring.Numeric{Decimal: mustParseDecimal(t, negNum), Status: pgtype.Present}) - } - - testutil.TestSuccessfulTranscodeEqFunc(t, "numeric", values, - func(aa, bb interface{}) bool { - a := aa.(shopspring.Numeric) - b := bb.(shopspring.Numeric) - - return a.Status == b.Status && a.Decimal.Equal(b.Decimal) - }) -} - -func TestNumericSet(t *testing.T) { - type _int8 int8 - - successfulTests := []struct { - source interface{} - result *shopspring.Numeric - }{ - {source: float32(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: float64(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: int8(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: int16(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: int32(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: int64(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: int8(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}}, - {source: int16(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}}, - {source: int32(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}}, - {source: int64(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}}, - {source: uint8(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: uint16(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: uint32(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: uint64(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: "1", result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: _int8(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: float64(1000), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1000"), Status: pgtype.Present}}, - {source: float64(1234), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1234"), Status: pgtype.Present}}, - {source: float64(12345678900), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "12345678900"), Status: pgtype.Present}}, - {source: float64(1.25), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1.25"), Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - r := &shopspring.Numeric{} - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !(r.Status == tt.result.Status && r.Decimal.Equal(tt.result.Decimal)) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestNumericAssignTo(t *testing.T) { - type _int8 int8 - - var i8 int8 - var i16 int16 - var i32 int32 - var i64 int64 - var i int - var ui8 uint8 - var ui16 uint16 - var ui32 uint32 - var ui64 uint64 - var ui uint - var pi8 *int8 - var _i8 _int8 - var _pi8 *_int8 - var f32 float32 - var f64 float64 - var pf32 *float32 - var pf64 *float64 - - simpleTests := []struct { - src *shopspring.Numeric - dst interface{} - expected interface{} - }{ - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &f32, expected: float32(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &f64, expected: float64(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "4.2"), Status: pgtype.Present}, dst: &f32, expected: float32(4.2)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "4.2"), Status: pgtype.Present}, dst: &f64, expected: float64(4.2)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42000"), Status: pgtype.Present}, dst: &i64, expected: int64(42000)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: &shopspring.Numeric{Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: &shopspring.Numeric{Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src *shopspring.Numeric - dst interface{} - expected interface{} - }{ - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &pf32, expected: float32(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &pf64, expected: float64(42)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src *shopspring.Numeric - dst interface{} - }{ - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "150"), Status: pgtype.Present}, dst: &i8}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "40000"), Status: pgtype.Present}, dst: &i16}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui8}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui16}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui32}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui64}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui}, - {src: &shopspring.Numeric{Status: pgtype.Null}, dst: &i32}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/float4.go b/pgtype/float4.go deleted file mode 100644 index 0947f36a..00000000 --- a/pgtype/float4.go +++ /dev/null @@ -1,197 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "math" - "strconv" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Float4 struct { - Float float32 - Status Status -} - -func (dst *Float4) Set(src interface{}) error { - if src == nil { - *dst = Float4{Status: Null} - return nil - } - - switch value := src.(type) { - case float32: - *dst = Float4{Float: value, Status: Present} - case float64: - *dst = Float4{Float: float32(value), Status: Present} - case int8: - *dst = Float4{Float: float32(value), Status: Present} - case uint8: - *dst = Float4{Float: float32(value), Status: Present} - case int16: - *dst = Float4{Float: float32(value), Status: Present} - case uint16: - *dst = Float4{Float: float32(value), Status: Present} - case int32: - f32 := float32(value) - if int32(f32) == value { - *dst = Float4{Float: f32, Status: Present} - } else { - return errors.Errorf("%v cannot be exactly represented as float32", value) - } - case uint32: - f32 := float32(value) - if uint32(f32) == value { - *dst = Float4{Float: f32, Status: Present} - } else { - return errors.Errorf("%v cannot be exactly represented as float32", value) - } - case int64: - f32 := float32(value) - if int64(f32) == value { - *dst = Float4{Float: f32, Status: Present} - } else { - return errors.Errorf("%v cannot be exactly represented as float32", value) - } - case uint64: - f32 := float32(value) - if uint64(f32) == value { - *dst = Float4{Float: f32, Status: Present} - } else { - return errors.Errorf("%v cannot be exactly represented as float32", value) - } - case int: - f32 := float32(value) - if int(f32) == value { - *dst = Float4{Float: f32, Status: Present} - } else { - return errors.Errorf("%v cannot be exactly represented as float32", value) - } - case uint: - f32 := float32(value) - if uint(f32) == value { - *dst = Float4{Float: f32, Status: Present} - } else { - return errors.Errorf("%v cannot be exactly represented as float32", value) - } - case string: - num, err := strconv.ParseFloat(value, 32) - if err != nil { - return err - } - *dst = Float4{Float: float32(num), Status: Present} - default: - if originalSrc, ok := underlyingNumberType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Float8", value) - } - - return nil -} - -func (dst *Float4) Get() interface{} { - switch dst.Status { - case Present: - return dst.Float - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Float4) AssignTo(dst interface{}) error { - return float64AssignTo(float64(src.Float), src.Status, dst) -} - -func (dst *Float4) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Float4{Status: Null} - return nil - } - - n, err := strconv.ParseFloat(string(src), 32) - if err != nil { - return err - } - - *dst = Float4{Float: float32(n), Status: Present} - return nil -} - -func (dst *Float4) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Float4{Status: Null} - return nil - } - - if len(src) != 4 { - return errors.Errorf("invalid length for float4: %v", len(src)) - } - - n := int32(binary.BigEndian.Uint32(src)) - - *dst = Float4{Float: math.Float32frombits(uint32(n)), Status: Present} - return nil -} - -func (src *Float4) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = append(buf, strconv.FormatFloat(float64(src.Float), 'f', -1, 32)...) - return buf, nil -} - -func (src *Float4) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = pgio.AppendUint32(buf, math.Float32bits(src.Float)) - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Float4) Scan(src interface{}) error { - if src == nil { - *dst = Float4{Status: Null} - return nil - } - - switch src := src.(type) { - case float64: - *dst = Float4{Float: float32(src), Status: Present} - return nil - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Float4) Value() (driver.Value, error) { - switch src.Status { - case Present: - return float64(src.Float), nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} diff --git a/pgtype/float4_array.go b/pgtype/float4_array.go deleted file mode 100644 index ef134407..00000000 --- a/pgtype/float4_array.go +++ /dev/null @@ -1,301 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Float4Array struct { - Elements []Float4 - Dimensions []ArrayDimension - Status Status -} - -func (dst *Float4Array) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = Float4Array{Status: Null} - return nil - } - - switch value := src.(type) { - - case []float32: - if value == nil { - *dst = Float4Array{Status: Null} - } else if len(value) == 0 { - *dst = Float4Array{Status: Present} - } else { - elements := make([]Float4, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = Float4Array{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Float4Array", value) - } - - return nil -} - -func (dst *Float4Array) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Float4Array) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]float32: - *v = make([]float32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Float4Array) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Float4Array{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Float4 - - if len(uta.Elements) > 0 { - elements = make([]Float4, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Float4 - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = Float4Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *Float4Array) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Float4Array{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = Float4Array{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Float4, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = Float4Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *Float4Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *Float4Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("float4"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "float4") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Float4Array) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Float4Array) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/float4_array_test.go b/pgtype/float4_array_test.go deleted file mode 100644 index 24d544b6..00000000 --- a/pgtype/float4_array_test.go +++ /dev/null @@ -1,152 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestFloat4ArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "float4[]", []interface{}{ - &pgtype.Float4Array{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.Float4Array{ - Elements: []pgtype.Float4{ - {Float: 1, Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.Float4Array{Status: pgtype.Null}, - &pgtype.Float4Array{ - Elements: []pgtype.Float4{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Float: 6, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.Float4Array{ - Elements: []pgtype.Float4{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestFloat4ArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Float4Array - }{ - { - source: []float32{1}, - result: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Float: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]float32)(nil)), - result: pgtype.Float4Array{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.Float4Array - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestFloat4ArrayAssignTo(t *testing.T) { - var float32Slice []float32 - var namedFloat32Slice _float32Slice - - simpleTests := []struct { - src pgtype.Float4Array - dst interface{} - expected interface{} - }{ - { - src: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Float: 1.23, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &float32Slice, - expected: []float32{1.23}, - }, - { - src: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Float: 1.23, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &namedFloat32Slice, - expected: _float32Slice{1.23}, - }, - { - src: pgtype.Float4Array{Status: pgtype.Null}, - dst: &float32Slice, - expected: (([]float32)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Float4Array - dst interface{} - }{ - { - src: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &float32Slice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } - -} diff --git a/pgtype/float4_test.go b/pgtype/float4_test.go deleted file mode 100644 index 4779b357..00000000 --- a/pgtype/float4_test.go +++ /dev/null @@ -1,149 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestFloat4Transcode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "float4", []interface{}{ - &pgtype.Float4{Float: -1, Status: pgtype.Present}, - &pgtype.Float4{Float: 0, Status: pgtype.Present}, - &pgtype.Float4{Float: 0.00001, Status: pgtype.Present}, - &pgtype.Float4{Float: 1, Status: pgtype.Present}, - &pgtype.Float4{Float: 9999.99, Status: pgtype.Present}, - &pgtype.Float4{Float: 0, Status: pgtype.Null}, - }) -} - -func TestFloat4Set(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Float4 - }{ - {source: float32(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: float64(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: int8(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: int16(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: int32(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: int64(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: int8(-1), result: pgtype.Float4{Float: -1, Status: pgtype.Present}}, - {source: int16(-1), result: pgtype.Float4{Float: -1, Status: pgtype.Present}}, - {source: int32(-1), result: pgtype.Float4{Float: -1, Status: pgtype.Present}}, - {source: int64(-1), result: pgtype.Float4{Float: -1, Status: pgtype.Present}}, - {source: uint8(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: uint16(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: uint32(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: uint64(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: "1", result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: _int8(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var r pgtype.Float4 - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestFloat4AssignTo(t *testing.T) { - var i8 int8 - var i16 int16 - var i32 int32 - var i64 int64 - var i int - var ui8 uint8 - var ui16 uint16 - var ui32 uint32 - var ui64 uint64 - var ui uint - var pi8 *int8 - var _i8 _int8 - var _pi8 *_int8 - var f32 float32 - var f64 float64 - var pf32 *float32 - var pf64 *float64 - - simpleTests := []struct { - src pgtype.Float4 - dst interface{} - expected interface{} - }{ - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &f32, expected: float32(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &f64, expected: float64(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: pgtype.Float4{Float: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: pgtype.Float4{Float: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.Float4 - dst interface{} - expected interface{} - }{ - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &pf32, expected: float32(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &pf64, expected: float64(42)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Float4 - dst interface{} - }{ - {src: pgtype.Float4{Float: 150, Status: pgtype.Present}, dst: &i8}, - {src: pgtype.Float4{Float: 40000, Status: pgtype.Present}, dst: &i16}, - {src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui8}, - {src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui16}, - {src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui32}, - {src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui64}, - {src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui}, - {src: pgtype.Float4{Float: 0, Status: pgtype.Null}, dst: &i32}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/float8.go b/pgtype/float8.go deleted file mode 100644 index 87cf6adb..00000000 --- a/pgtype/float8.go +++ /dev/null @@ -1,187 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "math" - "strconv" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Float8 struct { - Float float64 - Status Status -} - -func (dst *Float8) Set(src interface{}) error { - if src == nil { - *dst = Float8{Status: Null} - return nil - } - - switch value := src.(type) { - case float32: - *dst = Float8{Float: float64(value), Status: Present} - case float64: - *dst = Float8{Float: value, Status: Present} - case int8: - *dst = Float8{Float: float64(value), Status: Present} - case uint8: - *dst = Float8{Float: float64(value), Status: Present} - case int16: - *dst = Float8{Float: float64(value), Status: Present} - case uint16: - *dst = Float8{Float: float64(value), Status: Present} - case int32: - *dst = Float8{Float: float64(value), Status: Present} - case uint32: - *dst = Float8{Float: float64(value), Status: Present} - case int64: - f64 := float64(value) - if int64(f64) == value { - *dst = Float8{Float: f64, Status: Present} - } else { - return errors.Errorf("%v cannot be exactly represented as float64", value) - } - case uint64: - f64 := float64(value) - if uint64(f64) == value { - *dst = Float8{Float: f64, Status: Present} - } else { - return errors.Errorf("%v cannot be exactly represented as float64", value) - } - case int: - f64 := float64(value) - if int(f64) == value { - *dst = Float8{Float: f64, Status: Present} - } else { - return errors.Errorf("%v cannot be exactly represented as float64", value) - } - case uint: - f64 := float64(value) - if uint(f64) == value { - *dst = Float8{Float: f64, Status: Present} - } else { - return errors.Errorf("%v cannot be exactly represented as float64", value) - } - case string: - num, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - *dst = Float8{Float: float64(num), Status: Present} - default: - if originalSrc, ok := underlyingNumberType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Float8", value) - } - - return nil -} - -func (dst *Float8) Get() interface{} { - switch dst.Status { - case Present: - return dst.Float - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Float8) AssignTo(dst interface{}) error { - return float64AssignTo(src.Float, src.Status, dst) -} - -func (dst *Float8) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Float8{Status: Null} - return nil - } - - n, err := strconv.ParseFloat(string(src), 64) - if err != nil { - return err - } - - *dst = Float8{Float: n, Status: Present} - return nil -} - -func (dst *Float8) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Float8{Status: Null} - return nil - } - - if len(src) != 8 { - return errors.Errorf("invalid length for float4: %v", len(src)) - } - - n := int64(binary.BigEndian.Uint64(src)) - - *dst = Float8{Float: math.Float64frombits(uint64(n)), Status: Present} - return nil -} - -func (src *Float8) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = append(buf, strconv.FormatFloat(float64(src.Float), 'f', -1, 64)...) - return buf, nil -} - -func (src *Float8) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = pgio.AppendUint64(buf, math.Float64bits(src.Float)) - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Float8) Scan(src interface{}) error { - if src == nil { - *dst = Float8{Status: Null} - return nil - } - - switch src := src.(type) { - case float64: - *dst = Float8{Float: src, Status: Present} - return nil - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Float8) Value() (driver.Value, error) { - switch src.Status { - case Present: - return src.Float, nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} diff --git a/pgtype/float8_array.go b/pgtype/float8_array.go deleted file mode 100644 index ba63449c..00000000 --- a/pgtype/float8_array.go +++ /dev/null @@ -1,301 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Float8Array struct { - Elements []Float8 - Dimensions []ArrayDimension - Status Status -} - -func (dst *Float8Array) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = Float8Array{Status: Null} - return nil - } - - switch value := src.(type) { - - case []float64: - if value == nil { - *dst = Float8Array{Status: Null} - } else if len(value) == 0 { - *dst = Float8Array{Status: Present} - } else { - elements := make([]Float8, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = Float8Array{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Float8Array", value) - } - - return nil -} - -func (dst *Float8Array) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Float8Array) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]float64: - *v = make([]float64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Float8Array) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Float8Array{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Float8 - - if len(uta.Elements) > 0 { - elements = make([]Float8, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Float8 - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = Float8Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *Float8Array) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Float8Array{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = Float8Array{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Float8, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = Float8Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *Float8Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *Float8Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("float8"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "float8") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Float8Array) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Float8Array) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/float8_array_test.go b/pgtype/float8_array_test.go deleted file mode 100644 index b3e7a197..00000000 --- a/pgtype/float8_array_test.go +++ /dev/null @@ -1,152 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestFloat8ArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "float8[]", []interface{}{ - &pgtype.Float8Array{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.Float8Array{ - Elements: []pgtype.Float8{ - {Float: 1, Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.Float8Array{Status: pgtype.Null}, - &pgtype.Float8Array{ - Elements: []pgtype.Float8{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Float: 6, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.Float8Array{ - Elements: []pgtype.Float8{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestFloat8ArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Float8Array - }{ - { - source: []float64{1}, - result: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Float: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]float64)(nil)), - result: pgtype.Float8Array{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.Float8Array - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestFloat8ArrayAssignTo(t *testing.T) { - var float64Slice []float64 - var namedFloat64Slice _float64Slice - - simpleTests := []struct { - src pgtype.Float8Array - dst interface{} - expected interface{} - }{ - { - src: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Float: 1.23, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &float64Slice, - expected: []float64{1.23}, - }, - { - src: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Float: 1.23, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &namedFloat64Slice, - expected: _float64Slice{1.23}, - }, - { - src: pgtype.Float8Array{Status: pgtype.Null}, - dst: &float64Slice, - expected: (([]float64)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Float8Array - dst interface{} - }{ - { - src: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &float64Slice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } - -} diff --git a/pgtype/float8_test.go b/pgtype/float8_test.go deleted file mode 100644 index 15092916..00000000 --- a/pgtype/float8_test.go +++ /dev/null @@ -1,149 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestFloat8Transcode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "float8", []interface{}{ - &pgtype.Float8{Float: -1, Status: pgtype.Present}, - &pgtype.Float8{Float: 0, Status: pgtype.Present}, - &pgtype.Float8{Float: 0.00001, Status: pgtype.Present}, - &pgtype.Float8{Float: 1, Status: pgtype.Present}, - &pgtype.Float8{Float: 9999.99, Status: pgtype.Present}, - &pgtype.Float8{Float: 0, Status: pgtype.Null}, - }) -} - -func TestFloat8Set(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Float8 - }{ - {source: float32(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: float64(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: int8(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: int16(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: int32(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: int64(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: int8(-1), result: pgtype.Float8{Float: -1, Status: pgtype.Present}}, - {source: int16(-1), result: pgtype.Float8{Float: -1, Status: pgtype.Present}}, - {source: int32(-1), result: pgtype.Float8{Float: -1, Status: pgtype.Present}}, - {source: int64(-1), result: pgtype.Float8{Float: -1, Status: pgtype.Present}}, - {source: uint8(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: uint16(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: uint32(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: uint64(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: "1", result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: _int8(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var r pgtype.Float8 - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestFloat8AssignTo(t *testing.T) { - var i8 int8 - var i16 int16 - var i32 int32 - var i64 int64 - var i int - var ui8 uint8 - var ui16 uint16 - var ui32 uint32 - var ui64 uint64 - var ui uint - var pi8 *int8 - var _i8 _int8 - var _pi8 *_int8 - var f32 float32 - var f64 float64 - var pf32 *float32 - var pf64 *float64 - - simpleTests := []struct { - src pgtype.Float8 - dst interface{} - expected interface{} - }{ - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &f32, expected: float32(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &f64, expected: float64(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: pgtype.Float8{Float: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: pgtype.Float8{Float: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.Float8 - dst interface{} - expected interface{} - }{ - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &pf32, expected: float32(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &pf64, expected: float64(42)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Float8 - dst interface{} - }{ - {src: pgtype.Float8{Float: 150, Status: pgtype.Present}, dst: &i8}, - {src: pgtype.Float8{Float: 40000, Status: pgtype.Present}, dst: &i16}, - {src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui8}, - {src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui16}, - {src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui32}, - {src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui64}, - {src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui}, - {src: pgtype.Float8{Float: 0, Status: pgtype.Null}, dst: &i32}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/generic_binary.go b/pgtype/generic_binary.go deleted file mode 100644 index 2596ecae..00000000 --- a/pgtype/generic_binary.go +++ /dev/null @@ -1,39 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" -) - -// GenericBinary is a placeholder for binary format values that no other type exists -// to handle. -type GenericBinary Bytea - -func (dst *GenericBinary) Set(src interface{}) error { - return (*Bytea)(dst).Set(src) -} - -func (dst *GenericBinary) Get() interface{} { - return (*Bytea)(dst).Get() -} - -func (src *GenericBinary) AssignTo(dst interface{}) error { - return (*Bytea)(src).AssignTo(dst) -} - -func (dst *GenericBinary) DecodeBinary(ci *ConnInfo, src []byte) error { - return (*Bytea)(dst).DecodeBinary(ci, src) -} - -func (src *GenericBinary) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*Bytea)(src).EncodeBinary(ci, buf) -} - -// Scan implements the database/sql Scanner interface. -func (dst *GenericBinary) Scan(src interface{}) error { - return (*Bytea)(dst).Scan(src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *GenericBinary) Value() (driver.Value, error) { - return (*Bytea)(src).Value() -} diff --git a/pgtype/generic_text.go b/pgtype/generic_text.go deleted file mode 100644 index 0e3db9de..00000000 --- a/pgtype/generic_text.go +++ /dev/null @@ -1,39 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" -) - -// GenericText is a placeholder for text format values that no other type exists -// to handle. -type GenericText Text - -func (dst *GenericText) Set(src interface{}) error { - return (*Text)(dst).Set(src) -} - -func (dst *GenericText) Get() interface{} { - return (*Text)(dst).Get() -} - -func (src *GenericText) AssignTo(dst interface{}) error { - return (*Text)(src).AssignTo(dst) -} - -func (dst *GenericText) DecodeText(ci *ConnInfo, src []byte) error { - return (*Text)(dst).DecodeText(ci, src) -} - -func (src *GenericText) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*Text)(src).EncodeText(ci, buf) -} - -// Scan implements the database/sql Scanner interface. -func (dst *GenericText) Scan(src interface{}) error { - return (*Text)(dst).Scan(src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *GenericText) Value() (driver.Value, error) { - return (*Text)(src).Value() -} diff --git a/pgtype/hstore.go b/pgtype/hstore.go deleted file mode 100644 index 522813ff..00000000 --- a/pgtype/hstore.go +++ /dev/null @@ -1,435 +0,0 @@ -package pgtype - -import ( - "bytes" - "database/sql/driver" - "encoding/binary" - "strings" - "unicode" - "unicode/utf8" - - errors "golang.org/x/xerrors" - - "github.com/jackc/pgio" -) - -// Hstore represents an hstore column that can be null or have null values -// associated with its keys. -type Hstore struct { - Map map[string]Text - Status Status -} - -func (dst *Hstore) Set(src interface{}) error { - if src == nil { - *dst = Hstore{Status: Null} - return nil - } - - switch value := src.(type) { - case map[string]string: - m := make(map[string]Text, len(value)) - for k, v := range value { - m[k] = Text{String: v, Status: Present} - } - *dst = Hstore{Map: m, Status: Present} - default: - return errors.Errorf("cannot convert %v to Hstore", src) - } - - return nil -} - -func (dst *Hstore) Get() interface{} { - switch dst.Status { - case Present: - return dst.Map - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Hstore) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *map[string]string: - *v = make(map[string]string, len(src.Map)) - for k, val := range src.Map { - if val.Status != Present { - return errors.Errorf("cannot decode %#v into %T", src, dst) - } - (*v)[k] = val.String - } - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Hstore) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Hstore{Status: Null} - return nil - } - - keys, values, err := parseHstore(string(src)) - if err != nil { - return err - } - - m := make(map[string]Text, len(keys)) - for i := range keys { - m[keys[i]] = values[i] - } - - *dst = Hstore{Map: m, Status: Present} - return nil -} - -func (dst *Hstore) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Hstore{Status: Null} - return nil - } - - rp := 0 - - if len(src[rp:]) < 4 { - return errors.Errorf("hstore incomplete %v", src) - } - pairCount := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - - m := make(map[string]Text, pairCount) - - for i := 0; i < pairCount; i++ { - if len(src[rp:]) < 4 { - return errors.Errorf("hstore incomplete %v", src) - } - keyLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - - if len(src[rp:]) < keyLen { - return errors.Errorf("hstore incomplete %v", src) - } - key := string(src[rp : rp+keyLen]) - rp += keyLen - - if len(src[rp:]) < 4 { - return errors.Errorf("hstore incomplete %v", src) - } - valueLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - - var valueBuf []byte - if valueLen >= 0 { - valueBuf = src[rp : rp+valueLen] - } - rp += valueLen - - var value Text - err := value.DecodeBinary(ci, valueBuf) - if err != nil { - return err - } - m[key] = value - } - - *dst = Hstore{Map: m, Status: Present} - - return nil -} - -func (src *Hstore) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - firstPair := true - - for k, v := range src.Map { - if firstPair { - firstPair = false - } else { - buf = append(buf, ',') - } - - buf = append(buf, quoteHstoreElementIfNeeded(k)...) - buf = append(buf, "=>"...) - - elemBuf, err := v.EncodeText(ci, nil) - if err != nil { - return nil, err - } - - if elemBuf == nil { - buf = append(buf, "NULL"...) - } else { - buf = append(buf, quoteHstoreElementIfNeeded(string(elemBuf))...) - } - } - - return buf, nil -} - -func (src *Hstore) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = pgio.AppendInt32(buf, int32(len(src.Map))) - - var err error - for k, v := range src.Map { - buf = pgio.AppendInt32(buf, int32(len(k))) - buf = append(buf, k...) - - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := v.EncodeText(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, err -} - -var quoteHstoreReplacer = strings.NewReplacer(`\`, `\\`, `"`, `\"`) - -func quoteHstoreElement(src string) string { - return `"` + quoteArrayReplacer.Replace(src) + `"` -} - -func quoteHstoreElementIfNeeded(src string) string { - if src == "" || (len(src) == 4 && strings.ToLower(src) == "null") || strings.ContainsAny(src, ` {},"\=>`) { - return quoteArrayElement(src) - } - return src -} - -const ( - hsPre = iota - hsKey - hsSep - hsVal - hsNul - hsNext -) - -type hstoreParser struct { - str string - pos int -} - -func newHSP(in string) *hstoreParser { - return &hstoreParser{ - pos: 0, - str: in, - } -} - -func (p *hstoreParser) Consume() (r rune, end bool) { - if p.pos >= len(p.str) { - end = true - return - } - r, w := utf8.DecodeRuneInString(p.str[p.pos:]) - p.pos += w - return -} - -func (p *hstoreParser) Peek() (r rune, end bool) { - if p.pos >= len(p.str) { - end = true - return - } - r, _ = utf8.DecodeRuneInString(p.str[p.pos:]) - return -} - -// parseHstore parses the string representation of an hstore column (the same -// you would get from an ordinary SELECT) into two slices of keys and values. it -// is used internally in the default parsing of hstores. -func parseHstore(s string) (k []string, v []Text, err error) { - if s == "" { - return - } - - buf := bytes.Buffer{} - keys := []string{} - values := []Text{} - p := newHSP(s) - - r, end := p.Consume() - state := hsPre - - for !end { - switch state { - case hsPre: - if r == '"' { - state = hsKey - } else { - err = errors.New("String does not begin with \"") - } - case hsKey: - switch r { - case '"': //End of the key - if buf.Len() == 0 { - err = errors.New("Empty Key is invalid") - } else { - keys = append(keys, buf.String()) - buf = bytes.Buffer{} - state = hsSep - } - case '\\': //Potential escaped character - n, end := p.Consume() - switch { - case end: - err = errors.New("Found EOS in key, expecting character or \"") - case n == '"', n == '\\': - buf.WriteRune(n) - default: - buf.WriteRune(r) - buf.WriteRune(n) - } - default: //Any other character - buf.WriteRune(r) - } - case hsSep: - if r == '=' { - r, end = p.Consume() - switch { - case end: - err = errors.New("Found EOS after '=', expecting '>'") - case r == '>': - r, end = p.Consume() - switch { - case end: - err = errors.New("Found EOS after '=>', expecting '\"' or 'NULL'") - case r == '"': - state = hsVal - case r == 'N': - state = hsNul - default: - err = errors.Errorf("Invalid character '%c' after '=>', expecting '\"' or 'NULL'", r) - } - default: - err = errors.Errorf("Invalid character after '=', expecting '>'") - } - } else { - err = errors.Errorf("Invalid character '%c' after value, expecting '='", r) - } - case hsVal: - switch r { - case '"': //End of the value - values = append(values, Text{String: buf.String(), Status: Present}) - buf = bytes.Buffer{} - state = hsNext - case '\\': //Potential escaped character - n, end := p.Consume() - switch { - case end: - err = errors.New("Found EOS in key, expecting character or \"") - case n == '"', n == '\\': - buf.WriteRune(n) - default: - buf.WriteRune(r) - buf.WriteRune(n) - } - default: //Any other character - buf.WriteRune(r) - } - case hsNul: - nulBuf := make([]rune, 3) - nulBuf[0] = r - for i := 1; i < 3; i++ { - r, end = p.Consume() - if end { - err = errors.New("Found EOS in NULL value") - return - } - nulBuf[i] = r - } - if nulBuf[0] == 'U' && nulBuf[1] == 'L' && nulBuf[2] == 'L' { - values = append(values, Text{Status: Null}) - state = hsNext - } else { - err = errors.Errorf("Invalid NULL value: 'N%s'", string(nulBuf)) - } - case hsNext: - if r == ',' { - r, end = p.Consume() - switch { - case end: - err = errors.New("Found EOS after ',', expcting space") - case (unicode.IsSpace(r)): - r, end = p.Consume() - state = hsKey - default: - err = errors.Errorf("Invalid character '%c' after ', ', expecting \"", r) - } - } else { - err = errors.Errorf("Invalid character '%c' after value, expecting ','", r) - } - } - - if err != nil { - return - } - r, end = p.Consume() - } - if state != hsNext { - err = errors.New("Improperly formatted hstore") - return - } - k = keys - v = values - return -} - -// Scan implements the database/sql Scanner interface. -func (dst *Hstore) Scan(src interface{}) error { - if src == nil { - *dst = Hstore{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Hstore) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/hstore_array.go b/pgtype/hstore_array.go deleted file mode 100644 index 1bdac816..00000000 --- a/pgtype/hstore_array.go +++ /dev/null @@ -1,301 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type HstoreArray struct { - Elements []Hstore - Dimensions []ArrayDimension - Status Status -} - -func (dst *HstoreArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = HstoreArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []map[string]string: - if value == nil { - *dst = HstoreArray{Status: Null} - } else if len(value) == 0 { - *dst = HstoreArray{Status: Present} - } else { - elements := make([]Hstore, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = HstoreArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to HstoreArray", value) - } - - return nil -} - -func (dst *HstoreArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *HstoreArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]map[string]string: - *v = make([]map[string]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *HstoreArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = HstoreArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Hstore - - if len(uta.Elements) > 0 { - elements = make([]Hstore, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Hstore - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = HstoreArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *HstoreArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = HstoreArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = HstoreArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Hstore, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = HstoreArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *HstoreArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *HstoreArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("hstore"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "hstore") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *HstoreArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *HstoreArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/hstore_array_test.go b/pgtype/hstore_array_test.go deleted file mode 100644 index bc45cbdf..00000000 --- a/pgtype/hstore_array_test.go +++ /dev/null @@ -1,199 +0,0 @@ -package pgtype_test - -import ( - "context" - "reflect" - "testing" - - "github.com/jackc/pgx/v4" - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestHstoreArrayTranscode(t *testing.T) { - conn := testutil.MustConnectPgx(t) - defer testutil.MustCloseContext(t, conn) - - var hstoreOID pgtype.OID - err := conn.QueryRow(context.Background(), "select t.oid from pg_type t where t.typname='hstore';").Scan(&hstoreOID) - if err != nil { - t.Fatalf("did not find hstore OID, %v", err) - } - conn.ConnInfo.RegisterDataType(pgtype.DataType{Value: &pgtype.Hstore{}, Name: "hstore", OID: hstoreOID}) - - var hstoreArrayOID pgtype.OID - err = conn.QueryRow(context.Background(), "select t.oid from pg_type t where t.typname='_hstore';").Scan(&hstoreArrayOID) - if err != nil { - t.Fatalf("did not find _hstore OID, %v", err) - } - conn.ConnInfo.RegisterDataType(pgtype.DataType{Value: &pgtype.HstoreArray{}, Name: "_hstore", OID: hstoreArrayOID}) - - text := func(s string) pgtype.Text { - return pgtype.Text{String: s, Status: pgtype.Present} - } - - values := []pgtype.Hstore{ - {Map: map[string]pgtype.Text{}, Status: pgtype.Present}, - {Map: map[string]pgtype.Text{"foo": text("bar")}, Status: pgtype.Present}, - {Map: map[string]pgtype.Text{"foo": text("bar"), "baz": text("quz")}, Status: pgtype.Present}, - {Map: map[string]pgtype.Text{"NULL": text("bar")}, Status: pgtype.Present}, - {Map: map[string]pgtype.Text{"foo": text("NULL")}, Status: pgtype.Present}, - {Status: pgtype.Null}, - } - - specialStrings := []string{ - `"`, - `'`, - `\`, - `\\`, - `=>`, - ` `, - `\ / / \\ => " ' " '`, - } - for _, s := range specialStrings { - // Special key values - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{s + "foo": text("bar")}, Status: pgtype.Present}) // at beginning - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s + "bar": text("bar")}, Status: pgtype.Present}) // in middle - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s: text("bar")}, Status: pgtype.Present}) // at end - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{s: text("bar")}, Status: pgtype.Present}) // is key - - // Special value values - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s + "bar")}, Status: pgtype.Present}) // at beginning - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s + "bar")}, Status: pgtype.Present}) // in middle - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s)}, Status: pgtype.Present}) // at end - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s)}, Status: pgtype.Present}) // is key - } - - src := &pgtype.HstoreArray{ - Elements: values, - Dimensions: []pgtype.ArrayDimension{{Length: int32(len(values)), LowerBound: 1}}, - Status: pgtype.Present, - } - - ps, err := conn.Prepare(context.Background(), "test", "select $1::hstore[]") - if err != nil { - t.Fatal(err) - } - - formats := []struct { - name string - formatCode int16 - }{ - {name: "TextFormat", formatCode: pgx.TextFormatCode}, - {name: "BinaryFormat", formatCode: pgx.BinaryFormatCode}, - } - - for _, fc := range formats { - ps.FieldDescriptions[0].FormatCode = fc.formatCode - vEncoder := testutil.ForceEncoder(src, fc.formatCode) - if vEncoder == nil { - t.Logf("%#v does not implement %v", src, fc.name) - continue - } - - var result pgtype.HstoreArray - err := conn.QueryRow(context.Background(), "test", vEncoder).Scan(&result) - if err != nil { - t.Errorf("%v: %v", fc.name, err) - continue - } - - if result.Status != src.Status { - t.Errorf("%v: expected Status %v, got %v", fc.formatCode, src.Status, result.Status) - continue - } - - if len(result.Elements) != len(src.Elements) { - t.Errorf("%v: expected %v elements, got %v", fc.formatCode, len(src.Elements), len(result.Elements)) - continue - } - - for i := range result.Elements { - a := src.Elements[i] - b := result.Elements[i] - - if a.Status != b.Status { - t.Errorf("%v element idx %d: expected status %v, got %v", fc.formatCode, i, a.Status, b.Status) - } - - if len(a.Map) != len(b.Map) { - t.Errorf("%v element idx %d: expected %v pairs, got %v", fc.formatCode, i, len(a.Map), len(b.Map)) - } - - for k := range a.Map { - if a.Map[k] != b.Map[k] { - t.Errorf("%v element idx %d: expected key %v to be %v, got %v", fc.formatCode, i, k, a.Map[k], b.Map[k]) - } - } - } - } -} - -func TestHstoreArraySet(t *testing.T) { - successfulTests := []struct { - src []map[string]string - result pgtype.HstoreArray - }{ - { - src: []map[string]string{{"foo": "bar"}}, - result: pgtype.HstoreArray{ - Elements: []pgtype.Hstore{ - { - Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, - Status: pgtype.Present, - }, - }, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - }, - } - - for i, tt := range successfulTests { - var dst pgtype.HstoreArray - err := dst.Set(tt.src) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(dst, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.src, tt.result, dst) - } - } -} - -func TestHstoreArrayAssignTo(t *testing.T) { - var m []map[string]string - - simpleTests := []struct { - src pgtype.HstoreArray - dst *[]map[string]string - expected []map[string]string - }{ - { - src: pgtype.HstoreArray{ - Elements: []pgtype.Hstore{ - { - Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, - Status: pgtype.Present, - }, - }, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &m, - expected: []map[string]string{{"foo": "bar"}}}, - {src: pgtype.HstoreArray{Status: pgtype.Null}, dst: &m, expected: (([]map[string]string)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(*tt.dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst) - } - } -} diff --git a/pgtype/hstore_test.go b/pgtype/hstore_test.go deleted file mode 100644 index 71fd2355..00000000 --- a/pgtype/hstore_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestHstoreTranscode(t *testing.T) { - text := func(s string) pgtype.Text { - return pgtype.Text{String: s, Status: pgtype.Present} - } - - values := []interface{}{ - &pgtype.Hstore{Map: map[string]pgtype.Text{}, Status: pgtype.Present}, - &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("bar")}, Status: pgtype.Present}, - &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("bar"), "baz": text("quz")}, Status: pgtype.Present}, - &pgtype.Hstore{Map: map[string]pgtype.Text{"NULL": text("bar")}, Status: pgtype.Present}, - &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("NULL")}, Status: pgtype.Present}, - &pgtype.Hstore{Status: pgtype.Null}, - } - - specialStrings := []string{ - `"`, - `'`, - `\`, - `\\`, - `=>`, - ` `, - `\ / / \\ => " ' " '`, - } - for _, s := range specialStrings { - // Special key values - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{s + "foo": text("bar")}, Status: pgtype.Present}) // at beginning - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s + "bar": text("bar")}, Status: pgtype.Present}) // in middle - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s: text("bar")}, Status: pgtype.Present}) // at end - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{s: text("bar")}, Status: pgtype.Present}) // is key - - // Special value values - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s + "bar")}, Status: pgtype.Present}) // at beginning - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s + "bar")}, Status: pgtype.Present}) // in middle - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s)}, Status: pgtype.Present}) // at end - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s)}, Status: pgtype.Present}) // is key - } - - testutil.TestSuccessfulTranscodeEqFunc(t, "hstore", values, func(ai, bi interface{}) bool { - a := ai.(pgtype.Hstore) - b := bi.(pgtype.Hstore) - - if len(a.Map) != len(b.Map) || a.Status != b.Status { - return false - } - - for k := range a.Map { - if a.Map[k] != b.Map[k] { - return false - } - } - - return true - }) -} - -func TestHstoreSet(t *testing.T) { - successfulTests := []struct { - src map[string]string - result pgtype.Hstore - }{ - {src: map[string]string{"foo": "bar"}, result: pgtype.Hstore{Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var dst pgtype.Hstore - err := dst.Set(tt.src) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(dst, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.src, tt.result, dst) - } - } -} - -func TestHstoreAssignTo(t *testing.T) { - var m map[string]string - - simpleTests := []struct { - src pgtype.Hstore - dst *map[string]string - expected map[string]string - }{ - {src: pgtype.Hstore{Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, Status: pgtype.Present}, dst: &m, expected: map[string]string{"foo": "bar"}}, - {src: pgtype.Hstore{Status: pgtype.Null}, dst: &m, expected: ((map[string]string)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(*tt.dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst) - } - } -} diff --git a/pgtype/inet.go b/pgtype/inet.go deleted file mode 100644 index 0fb1c418..00000000 --- a/pgtype/inet.go +++ /dev/null @@ -1,216 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "net" - - errors "golang.org/x/xerrors" -) - -// Network address family is dependent on server socket.h value for AF_INET. -// In practice, all platforms appear to have the same value. See -// src/include/utils/inet.h for more information. -const ( - defaultAFInet = 2 - defaultAFInet6 = 3 -) - -// Inet represents both inet and cidr PostgreSQL types. -type Inet struct { - IPNet *net.IPNet - Status Status -} - -func (dst *Inet) Set(src interface{}) error { - if src == nil { - *dst = Inet{Status: Null} - return nil - } - - switch value := src.(type) { - case net.IPNet: - *dst = Inet{IPNet: &value, Status: Present} - case *net.IPNet: - *dst = Inet{IPNet: value, Status: Present} - case net.IP: - bitCount := len(value) * 8 - mask := net.CIDRMask(bitCount, bitCount) - *dst = Inet{IPNet: &net.IPNet{Mask: mask, IP: value}, Status: Present} - case string: - _, ipnet, err := net.ParseCIDR(value) - if err != nil { - return err - } - *dst = Inet{IPNet: ipnet, Status: Present} - default: - if originalSrc, ok := underlyingPtrType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Inet", value) - } - - return nil -} - -func (dst *Inet) Get() interface{} { - switch dst.Status { - case Present: - return dst.IPNet - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Inet) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *net.IPNet: - *v = net.IPNet{ - IP: make(net.IP, len(src.IPNet.IP)), - Mask: make(net.IPMask, len(src.IPNet.Mask)), - } - copy(v.IP, src.IPNet.IP) - copy(v.Mask, src.IPNet.Mask) - return nil - case *net.IP: - if oneCount, bitCount := src.IPNet.Mask.Size(); oneCount != bitCount { - return errors.Errorf("cannot assign %v to %T", src, dst) - } - *v = make(net.IP, len(src.IPNet.IP)) - copy(*v, src.IPNet.IP) - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Inet) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Inet{Status: Null} - return nil - } - - var ipnet *net.IPNet - var err error - - if ip := net.ParseIP(string(src)); ip != nil { - ipv4 := ip.To4() - if ipv4 != nil { - ip = ipv4 - } - bitCount := len(ip) * 8 - mask := net.CIDRMask(bitCount, bitCount) - ipnet = &net.IPNet{Mask: mask, IP: ip} - } else { - _, ipnet, err = net.ParseCIDR(string(src)) - if err != nil { - return err - } - } - - *dst = Inet{IPNet: ipnet, Status: Present} - return nil -} - -func (dst *Inet) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Inet{Status: Null} - return nil - } - - if len(src) != 8 && len(src) != 20 { - return errors.Errorf("Received an invalid size for a inet: %d", len(src)) - } - - // ignore family - bits := src[1] - // ignore is_cidr - addressLength := src[3] - - var ipnet net.IPNet - ipnet.IP = make(net.IP, int(addressLength)) - copy(ipnet.IP, src[4:]) - ipnet.Mask = net.CIDRMask(int(bits), int(addressLength)*8) - - *dst = Inet{IPNet: &ipnet, Status: Present} - - return nil -} - -func (src *Inet) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, src.IPNet.String()...), nil -} - -// EncodeBinary encodes src into w. -func (src *Inet) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var family byte - switch len(src.IPNet.IP) { - case net.IPv4len: - family = defaultAFInet - case net.IPv6len: - family = defaultAFInet6 - default: - return nil, errors.Errorf("Unexpected IP length: %v", len(src.IPNet.IP)) - } - - buf = append(buf, family) - - ones, _ := src.IPNet.Mask.Size() - buf = append(buf, byte(ones)) - - // is_cidr is ignored on server - buf = append(buf, 0) - - buf = append(buf, byte(len(src.IPNet.IP))) - - return append(buf, src.IPNet.IP...), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Inet) Scan(src interface{}) error { - if src == nil { - *dst = Inet{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Inet) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/inet_array.go b/pgtype/inet_array.go deleted file mode 100644 index b31d3588..00000000 --- a/pgtype/inet_array.go +++ /dev/null @@ -1,330 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "net" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type InetArray struct { - Elements []Inet - Dimensions []ArrayDimension - Status Status -} - -func (dst *InetArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = InetArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []*net.IPNet: - if value == nil { - *dst = InetArray{Status: Null} - } else if len(value) == 0 { - *dst = InetArray{Status: Present} - } else { - elements := make([]Inet, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = InetArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - case []net.IP: - if value == nil { - *dst = InetArray{Status: Null} - } else if len(value) == 0 { - *dst = InetArray{Status: Present} - } else { - elements := make([]Inet, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = InetArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to InetArray", value) - } - - return nil -} - -func (dst *InetArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *InetArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]*net.IPNet: - *v = make([]*net.IPNet, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]net.IP: - *v = make([]net.IP, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *InetArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = InetArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Inet - - if len(uta.Elements) > 0 { - elements = make([]Inet, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Inet - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = InetArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *InetArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = InetArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = InetArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Inet, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = InetArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *InetArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *InetArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("inet"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "inet") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *InetArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *InetArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/inet_array_test.go b/pgtype/inet_array_test.go deleted file mode 100644 index 4e93d0f5..00000000 --- a/pgtype/inet_array_test.go +++ /dev/null @@ -1,165 +0,0 @@ -package pgtype_test - -import ( - "net" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestInetArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "inet[]", []interface{}{ - &pgtype.InetArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.InetArray{ - Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.InetArray{Status: pgtype.Null}, - &pgtype.InetArray{ - Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present}, - {Status: pgtype.Null}, - {IPNet: mustParseCIDR(t, "255.0.0.0/8"), Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.InetArray{ - Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestInetArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.InetArray - }{ - { - source: []*net.IPNet{mustParseCIDR(t, "127.0.0.1/32")}, - result: pgtype.InetArray{ - Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]*net.IPNet)(nil)), - result: pgtype.InetArray{Status: pgtype.Null}, - }, - { - source: []net.IP{mustParseCIDR(t, "127.0.0.1/32").IP}, - result: pgtype.InetArray{ - Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]net.IP)(nil)), - result: pgtype.InetArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.InetArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestInetArrayAssignTo(t *testing.T) { - var ipnetSlice []*net.IPNet - var ipSlice []net.IP - - simpleTests := []struct { - src pgtype.InetArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.InetArray{ - Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &ipnetSlice, - expected: []*net.IPNet{mustParseCIDR(t, "127.0.0.1/32")}, - }, - { - src: pgtype.InetArray{ - Elements: []pgtype.Inet{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &ipnetSlice, - expected: []*net.IPNet{nil}, - }, - { - src: pgtype.InetArray{ - Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &ipSlice, - expected: []net.IP{mustParseCIDR(t, "127.0.0.1/32").IP}, - }, - { - src: pgtype.InetArray{ - Elements: []pgtype.Inet{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &ipSlice, - expected: []net.IP{nil}, - }, - { - src: pgtype.InetArray{Status: pgtype.Null}, - dst: &ipnetSlice, - expected: (([]*net.IPNet)(nil)), - }, - { - src: pgtype.InetArray{Status: pgtype.Null}, - dst: &ipSlice, - expected: (([]net.IP)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } -} diff --git a/pgtype/inet_test.go b/pgtype/inet_test.go deleted file mode 100644 index ee93873b..00000000 --- a/pgtype/inet_test.go +++ /dev/null @@ -1,115 +0,0 @@ -package pgtype_test - -import ( - "net" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestInetTranscode(t *testing.T) { - for _, pgTypeName := range []string{"inet", "cidr"} { - testutil.TestSuccessfulTranscode(t, pgTypeName, []interface{}{ - &pgtype.Inet{IPNet: mustParseCIDR(t, "0.0.0.0/32"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "192.168.1.0/24"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "255.0.0.0/8"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "255.255.255.255/32"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "::/128"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "::/0"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "::1/128"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present}, - &pgtype.Inet{Status: pgtype.Null}, - }) - } -} - -func TestInetSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Inet - }{ - {source: mustParseCIDR(t, "127.0.0.1/32"), result: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - {source: mustParseCIDR(t, "127.0.0.1/32").IP, result: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - {source: "127.0.0.1/32", result: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var r pgtype.Inet - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestInetAssignTo(t *testing.T) { - var ipnet net.IPNet - var pipnet *net.IPNet - var ip net.IP - var pip *net.IP - - simpleTests := []struct { - src pgtype.Inet - dst interface{} - expected interface{} - }{ - {src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, dst: &ipnet, expected: *mustParseCIDR(t, "127.0.0.1/32")}, - {src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, dst: &ip, expected: mustParseCIDR(t, "127.0.0.1/32").IP}, - {src: pgtype.Inet{Status: pgtype.Null}, dst: &pipnet, expected: ((*net.IPNet)(nil))}, - {src: pgtype.Inet{Status: pgtype.Null}, dst: &pip, expected: ((*net.IP)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %#v, but result was %#v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.Inet - dst interface{} - expected interface{} - }{ - {src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, dst: &pipnet, expected: *mustParseCIDR(t, "127.0.0.1/32")}, - {src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, dst: &pip, expected: mustParseCIDR(t, "127.0.0.1/32").IP}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Inet - dst interface{} - }{ - {src: pgtype.Inet{IPNet: mustParseCIDR(t, "192.168.0.0/16"), Status: pgtype.Present}, dst: &ip}, - {src: pgtype.Inet{Status: pgtype.Null}, dst: &ipnet}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/int2.go b/pgtype/int2.go deleted file mode 100644 index bbf2952f..00000000 --- a/pgtype/int2.go +++ /dev/null @@ -1,209 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "math" - "strconv" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Int2 struct { - Int int16 - Status Status -} - -func (dst *Int2) Set(src interface{}) error { - if src == nil { - *dst = Int2{Status: Null} - return nil - } - - switch value := src.(type) { - case int8: - *dst = Int2{Int: int16(value), Status: Present} - case uint8: - *dst = Int2{Int: int16(value), Status: Present} - case int16: - *dst = Int2{Int: int16(value), Status: Present} - case uint16: - if value > math.MaxInt16 { - return errors.Errorf("%d is greater than maximum value for Int2", value) - } - *dst = Int2{Int: int16(value), Status: Present} - case int32: - if value < math.MinInt16 { - return errors.Errorf("%d is greater than maximum value for Int2", value) - } - if value > math.MaxInt16 { - return errors.Errorf("%d is greater than maximum value for Int2", value) - } - *dst = Int2{Int: int16(value), Status: Present} - case uint32: - if value > math.MaxInt16 { - return errors.Errorf("%d is greater than maximum value for Int2", value) - } - *dst = Int2{Int: int16(value), Status: Present} - case int64: - if value < math.MinInt16 { - return errors.Errorf("%d is greater than maximum value for Int2", value) - } - if value > math.MaxInt16 { - return errors.Errorf("%d is greater than maximum value for Int2", value) - } - *dst = Int2{Int: int16(value), Status: Present} - case uint64: - if value > math.MaxInt16 { - return errors.Errorf("%d is greater than maximum value for Int2", value) - } - *dst = Int2{Int: int16(value), Status: Present} - case int: - if value < math.MinInt16 { - return errors.Errorf("%d is greater than maximum value for Int2", value) - } - if value > math.MaxInt16 { - return errors.Errorf("%d is greater than maximum value for Int2", value) - } - *dst = Int2{Int: int16(value), Status: Present} - case uint: - if value > math.MaxInt16 { - return errors.Errorf("%d is greater than maximum value for Int2", value) - } - *dst = Int2{Int: int16(value), Status: Present} - case string: - num, err := strconv.ParseInt(value, 10, 16) - if err != nil { - return err - } - *dst = Int2{Int: int16(num), Status: Present} - default: - if originalSrc, ok := underlyingNumberType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Int2", value) - } - - return nil -} - -func (dst *Int2) Get() interface{} { - switch dst.Status { - case Present: - return dst.Int - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Int2) AssignTo(dst interface{}) error { - return int64AssignTo(int64(src.Int), src.Status, dst) -} - -func (dst *Int2) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int2{Status: Null} - return nil - } - - n, err := strconv.ParseInt(string(src), 10, 16) - if err != nil { - return err - } - - *dst = Int2{Int: int16(n), Status: Present} - return nil -} - -func (dst *Int2) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int2{Status: Null} - return nil - } - - if len(src) != 2 { - return errors.Errorf("invalid length for int2: %v", len(src)) - } - - n := int16(binary.BigEndian.Uint16(src)) - *dst = Int2{Int: n, Status: Present} - return nil -} - -func (src *Int2) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, strconv.FormatInt(int64(src.Int), 10)...), nil -} - -func (src *Int2) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return pgio.AppendInt16(buf, src.Int), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Int2) Scan(src interface{}) error { - if src == nil { - *dst = Int2{Status: Null} - return nil - } - - switch src := src.(type) { - case int64: - if src < math.MinInt16 { - return errors.Errorf("%d is greater than maximum value for Int2", src) - } - if src > math.MaxInt16 { - return errors.Errorf("%d is greater than maximum value for Int2", src) - } - *dst = Int2{Int: int16(src), Status: Present} - return nil - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Int2) Value() (driver.Value, error) { - switch src.Status { - case Present: - return int64(src.Int), nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} - -func (src *Int2) MarshalJSON() ([]byte, error) { - switch src.Status { - case Present: - return []byte(strconv.FormatInt(int64(src.Int), 10)), nil - case Null: - return []byte("null"), nil - case Undefined: - return nil, errUndefined - } - - return nil, errBadStatus -} diff --git a/pgtype/int2_array.go b/pgtype/int2_array.go deleted file mode 100644 index afb39513..00000000 --- a/pgtype/int2_array.go +++ /dev/null @@ -1,329 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Int2Array struct { - Elements []Int2 - Dimensions []ArrayDimension - Status Status -} - -func (dst *Int2Array) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = Int2Array{Status: Null} - return nil - } - - switch value := src.(type) { - - case []int16: - if value == nil { - *dst = Int2Array{Status: Null} - } else if len(value) == 0 { - *dst = Int2Array{Status: Present} - } else { - elements := make([]Int2, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = Int2Array{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - case []uint16: - if value == nil { - *dst = Int2Array{Status: Null} - } else if len(value) == 0 { - *dst = Int2Array{Status: Present} - } else { - elements := make([]Int2, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = Int2Array{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Int2Array", value) - } - - return nil -} - -func (dst *Int2Array) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Int2Array) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]int16: - *v = make([]int16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint16: - *v = make([]uint16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Int2Array) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int2Array{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Int2 - - if len(uta.Elements) > 0 { - elements = make([]Int2, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Int2 - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = Int2Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *Int2Array) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int2Array{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = Int2Array{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Int2, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = Int2Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *Int2Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *Int2Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("int2"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "int2") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Int2Array) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Int2Array) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/int2_array_test.go b/pgtype/int2_array_test.go deleted file mode 100644 index fb4f0d60..00000000 --- a/pgtype/int2_array_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestInt2ArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "int2[]", []interface{}{ - &pgtype.Int2Array{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.Int2Array{ - Elements: []pgtype.Int2{ - {Int: 1, Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.Int2Array{Status: pgtype.Null}, - &pgtype.Int2Array{ - Elements: []pgtype.Int2{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Int: 6, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.Int2Array{ - Elements: []pgtype.Int2{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestInt2ArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Int2Array - }{ - { - source: []int16{1}, - result: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: []uint16{1}, - result: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]int16)(nil)), - result: pgtype.Int2Array{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.Int2Array - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestInt2ArrayAssignTo(t *testing.T) { - var int16Slice []int16 - var uint16Slice []uint16 - var namedInt16Slice _int16Slice - - simpleTests := []struct { - src pgtype.Int2Array - dst interface{} - expected interface{} - }{ - { - src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &int16Slice, - expected: []int16{1}, - }, - { - src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &uint16Slice, - expected: []uint16{1}, - }, - { - src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &namedInt16Slice, - expected: _int16Slice{1}, - }, - { - src: pgtype.Int2Array{Status: pgtype.Null}, - dst: &int16Slice, - expected: (([]int16)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Int2Array - dst interface{} - }{ - { - src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &int16Slice, - }, - { - src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: -1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &uint16Slice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } - -} diff --git a/pgtype/int2_test.go b/pgtype/int2_test.go deleted file mode 100644 index ff4732f7..00000000 --- a/pgtype/int2_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package pgtype_test - -import ( - "math" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestInt2Transcode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "int2", []interface{}{ - &pgtype.Int2{Int: math.MinInt16, Status: pgtype.Present}, - &pgtype.Int2{Int: -1, Status: pgtype.Present}, - &pgtype.Int2{Int: 0, Status: pgtype.Present}, - &pgtype.Int2{Int: 1, Status: pgtype.Present}, - &pgtype.Int2{Int: math.MaxInt16, Status: pgtype.Present}, - &pgtype.Int2{Int: 0, Status: pgtype.Null}, - }) -} - -func TestInt2Set(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Int2 - }{ - {source: int8(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: int16(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: int32(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: int64(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: int8(-1), result: pgtype.Int2{Int: -1, Status: pgtype.Present}}, - {source: int16(-1), result: pgtype.Int2{Int: -1, Status: pgtype.Present}}, - {source: int32(-1), result: pgtype.Int2{Int: -1, Status: pgtype.Present}}, - {source: int64(-1), result: pgtype.Int2{Int: -1, Status: pgtype.Present}}, - {source: uint8(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: uint16(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: uint32(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: uint64(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: "1", result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: _int8(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var r pgtype.Int2 - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestInt2AssignTo(t *testing.T) { - var i8 int8 - var i16 int16 - var i32 int32 - var i64 int64 - var i int - var ui8 uint8 - var ui16 uint16 - var ui32 uint32 - var ui64 uint64 - var ui uint - var pi8 *int8 - var _i8 _int8 - var _pi8 *_int8 - - simpleTests := []struct { - src pgtype.Int2 - dst interface{} - expected interface{} - }{ - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i8, expected: int8(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: pgtype.Int2{Int: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: pgtype.Int2{Int: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.Int2 - dst interface{} - expected interface{} - }{ - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &pi8, expected: int8(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &_pi8, expected: _int8(42)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Int2 - dst interface{} - }{ - {src: pgtype.Int2{Int: 150, Status: pgtype.Present}, dst: &i8}, - {src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui8}, - {src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui16}, - {src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui32}, - {src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui64}, - {src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui}, - {src: pgtype.Int2{Int: 0, Status: pgtype.Null}, dst: &i16}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/int4.go b/pgtype/int4.go deleted file mode 100644 index cc34ce0a..00000000 --- a/pgtype/int4.go +++ /dev/null @@ -1,213 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "encoding/json" - "math" - "strconv" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Int4 struct { - Int int32 - Status Status -} - -func (dst *Int4) Set(src interface{}) error { - if src == nil { - *dst = Int4{Status: Null} - return nil - } - - switch value := src.(type) { - case int8: - *dst = Int4{Int: int32(value), Status: Present} - case uint8: - *dst = Int4{Int: int32(value), Status: Present} - case int16: - *dst = Int4{Int: int32(value), Status: Present} - case uint16: - *dst = Int4{Int: int32(value), Status: Present} - case int32: - *dst = Int4{Int: int32(value), Status: Present} - case uint32: - if value > math.MaxInt32 { - return errors.Errorf("%d is greater than maximum value for Int4", value) - } - *dst = Int4{Int: int32(value), Status: Present} - case int64: - if value < math.MinInt32 { - return errors.Errorf("%d is greater than maximum value for Int4", value) - } - if value > math.MaxInt32 { - return errors.Errorf("%d is greater than maximum value for Int4", value) - } - *dst = Int4{Int: int32(value), Status: Present} - case uint64: - if value > math.MaxInt32 { - return errors.Errorf("%d is greater than maximum value for Int4", value) - } - *dst = Int4{Int: int32(value), Status: Present} - case int: - if value < math.MinInt32 { - return errors.Errorf("%d is greater than maximum value for Int4", value) - } - if value > math.MaxInt32 { - return errors.Errorf("%d is greater than maximum value for Int4", value) - } - *dst = Int4{Int: int32(value), Status: Present} - case uint: - if value > math.MaxInt32 { - return errors.Errorf("%d is greater than maximum value for Int4", value) - } - *dst = Int4{Int: int32(value), Status: Present} - case string: - num, err := strconv.ParseInt(value, 10, 32) - if err != nil { - return err - } - *dst = Int4{Int: int32(num), Status: Present} - default: - if originalSrc, ok := underlyingNumberType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Int4", value) - } - - return nil -} - -func (dst *Int4) Get() interface{} { - switch dst.Status { - case Present: - return dst.Int - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Int4) AssignTo(dst interface{}) error { - return int64AssignTo(int64(src.Int), src.Status, dst) -} - -func (dst *Int4) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int4{Status: Null} - return nil - } - - n, err := strconv.ParseInt(string(src), 10, 32) - if err != nil { - return err - } - - *dst = Int4{Int: int32(n), Status: Present} - return nil -} - -func (dst *Int4) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int4{Status: Null} - return nil - } - - if len(src) != 4 { - return errors.Errorf("invalid length for int4: %v", len(src)) - } - - n := int32(binary.BigEndian.Uint32(src)) - *dst = Int4{Int: n, Status: Present} - return nil -} - -func (src *Int4) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, strconv.FormatInt(int64(src.Int), 10)...), nil -} - -func (src *Int4) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return pgio.AppendInt32(buf, src.Int), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Int4) Scan(src interface{}) error { - if src == nil { - *dst = Int4{Status: Null} - return nil - } - - switch src := src.(type) { - case int64: - if src < math.MinInt32 { - return errors.Errorf("%d is greater than maximum value for Int4", src) - } - if src > math.MaxInt32 { - return errors.Errorf("%d is greater than maximum value for Int4", src) - } - *dst = Int4{Int: int32(src), Status: Present} - return nil - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Int4) Value() (driver.Value, error) { - switch src.Status { - case Present: - return int64(src.Int), nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} - -func (src *Int4) MarshalJSON() ([]byte, error) { - switch src.Status { - case Present: - return []byte(strconv.FormatInt(int64(src.Int), 10)), nil - case Null: - return []byte("null"), nil - case Undefined: - return nil, errUndefined - } - - return nil, errBadStatus -} - -func (dst *Int4) UnmarshalJSON(b []byte) error { - var n int32 - err := json.Unmarshal(b, &n) - if err != nil { - return err - } - - *dst = Int4{Int: n, Status: Present} - - return nil -} diff --git a/pgtype/int4_array.go b/pgtype/int4_array.go deleted file mode 100644 index bd0babb9..00000000 --- a/pgtype/int4_array.go +++ /dev/null @@ -1,348 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Int4Array struct { - Elements []Int4 - Dimensions []ArrayDimension - Status Status -} - -func (dst *Int4Array) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = Int4Array{Status: Null} - return nil - } - - switch value := src.(type) { - - case []int: - if value == nil { - *dst = Int4Array{Status: Null} - } else if len(value) == 0 { - *dst = Int4Array{Status: Present} - } else { - elements := make([]Int4, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = Int4Array{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - case []int32: - if value == nil { - *dst = Int4Array{Status: Null} - } else if len(value) == 0 { - *dst = Int4Array{Status: Present} - } else { - elements := make([]Int4, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = Int4Array{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - case []uint32: - if value == nil { - *dst = Int4Array{Status: Null} - } else if len(value) == 0 { - *dst = Int4Array{Status: Present} - } else { - elements := make([]Int4, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = Int4Array{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Int4Array", value) - } - - return nil -} - -func (dst *Int4Array) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Int4Array) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]int32: - *v = make([]int32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint32: - *v = make([]uint32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Int4Array) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int4Array{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Int4 - - if len(uta.Elements) > 0 { - elements = make([]Int4, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Int4 - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = Int4Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *Int4Array) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int4Array{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = Int4Array{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Int4, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = Int4Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *Int4Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *Int4Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("int4"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "int4") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Int4Array) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Int4Array) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/int4_array_test.go b/pgtype/int4_array_test.go deleted file mode 100644 index 06772cf6..00000000 --- a/pgtype/int4_array_test.go +++ /dev/null @@ -1,198 +0,0 @@ -package pgtype_test - -import ( - "math" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestInt4ArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "int4[]", []interface{}{ - &pgtype.Int4Array{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.Int4Array{ - Elements: []pgtype.Int4{ - {Int: 1, Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.Int4Array{Status: pgtype.Null}, - &pgtype.Int4Array{ - Elements: []pgtype.Int4{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Int: 6, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.Int4Array{ - Elements: []pgtype.Int4{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestInt4ArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Int4Array - expectedError bool - }{ - { - source: []int32{1}, - result: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: []int{1}, - result: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: []int{1, math.MaxInt32 + 1, 2}, - expectedError: true, - }, - { - source: []uint32{1}, - result: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]int32)(nil)), - result: pgtype.Int4Array{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.Int4Array - err := r.Set(tt.source) - if err != nil { - if tt.expectedError { - continue - } - t.Errorf("%d: %v", i, err) - } - - if tt.expectedError { - t.Errorf("%d: an error was expected, %v", i, tt) - continue - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestInt4ArrayAssignTo(t *testing.T) { - var int32Slice []int32 - var uint32Slice []uint32 - var namedInt32Slice _int32Slice - - simpleTests := []struct { - src pgtype.Int4Array - dst interface{} - expected interface{} - }{ - { - src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &int32Slice, - expected: []int32{1}, - }, - { - src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &uint32Slice, - expected: []uint32{1}, - }, - { - src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &namedInt32Slice, - expected: _int32Slice{1}, - }, - { - src: pgtype.Int4Array{Status: pgtype.Null}, - dst: &int32Slice, - expected: (([]int32)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Int4Array - dst interface{} - }{ - { - src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &int32Slice, - }, - { - src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: -1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &uint32Slice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } - -} diff --git a/pgtype/int4_test.go b/pgtype/int4_test.go deleted file mode 100644 index 6b23c5a9..00000000 --- a/pgtype/int4_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package pgtype_test - -import ( - "math" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestInt4Transcode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "int4", []interface{}{ - &pgtype.Int4{Int: math.MinInt32, Status: pgtype.Present}, - &pgtype.Int4{Int: -1, Status: pgtype.Present}, - &pgtype.Int4{Int: 0, Status: pgtype.Present}, - &pgtype.Int4{Int: 1, Status: pgtype.Present}, - &pgtype.Int4{Int: math.MaxInt32, Status: pgtype.Present}, - &pgtype.Int4{Int: 0, Status: pgtype.Null}, - }) -} - -func TestInt4Set(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Int4 - }{ - {source: int8(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: int16(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: int32(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: int64(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: int8(-1), result: pgtype.Int4{Int: -1, Status: pgtype.Present}}, - {source: int16(-1), result: pgtype.Int4{Int: -1, Status: pgtype.Present}}, - {source: int32(-1), result: pgtype.Int4{Int: -1, Status: pgtype.Present}}, - {source: int64(-1), result: pgtype.Int4{Int: -1, Status: pgtype.Present}}, - {source: uint8(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: uint16(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: uint32(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: uint64(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: "1", result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: _int8(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var r pgtype.Int4 - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestInt4AssignTo(t *testing.T) { - var i8 int8 - var i16 int16 - var i32 int32 - var i64 int64 - var i int - var ui8 uint8 - var ui16 uint16 - var ui32 uint32 - var ui64 uint64 - var ui uint - var pi8 *int8 - var _i8 _int8 - var _pi8 *_int8 - - simpleTests := []struct { - src pgtype.Int4 - dst interface{} - expected interface{} - }{ - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i8, expected: int8(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: pgtype.Int4{Int: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: pgtype.Int4{Int: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.Int4 - dst interface{} - expected interface{} - }{ - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &pi8, expected: int8(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &_pi8, expected: _int8(42)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Int4 - dst interface{} - }{ - {src: pgtype.Int4{Int: 150, Status: pgtype.Present}, dst: &i8}, - {src: pgtype.Int4{Int: 40000, Status: pgtype.Present}, dst: &i16}, - {src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui8}, - {src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui16}, - {src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui32}, - {src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui64}, - {src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui}, - {src: pgtype.Int4{Int: 0, Status: pgtype.Null}, dst: &i32}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/int4range.go b/pgtype/int4range.go deleted file mode 100644 index 03970ae6..00000000 --- a/pgtype/int4range.go +++ /dev/null @@ -1,250 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Int4range struct { - Lower Int4 - Upper Int4 - LowerType BoundType - UpperType BoundType - Status Status -} - -func (dst *Int4range) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Int4range", src) -} - -func (dst *Int4range) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Int4range) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Int4range) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int4range{Status: Null} - return nil - } - - utr, err := ParseUntypedTextRange(string(src)) - if err != nil { - return err - } - - *dst = Int4range{Status: Present} - - dst.LowerType = utr.LowerType - dst.UpperType = utr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeText(ci, []byte(utr.Lower)); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeText(ci, []byte(utr.Upper)); err != nil { - return err - } - } - - return nil -} - -func (dst *Int4range) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int4range{Status: Null} - return nil - } - - ubr, err := ParseUntypedBinaryRange(src) - if err != nil { - return err - } - - *dst = Int4range{Status: Present} - - dst.LowerType = ubr.LowerType - dst.UpperType = ubr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeBinary(ci, ubr.Lower); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeBinary(ci, ubr.Upper); err != nil { - return err - } - } - - return nil -} - -func (src Int4range) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - switch src.LowerType { - case Exclusive, Unbounded: - buf = append(buf, '(') - case Inclusive: - buf = append(buf, '[') - case Empty: - return append(buf, "empty"...), nil - default: - return nil, errors.Errorf("unknown lower bound type %v", src.LowerType) - } - - var err error - - if src.LowerType != Unbounded { - buf, err = src.Lower.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - } - - buf = append(buf, ',') - - if src.UpperType != Unbounded { - buf, err = src.Upper.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - } - - switch src.UpperType { - case Exclusive, Unbounded: - buf = append(buf, ')') - case Inclusive: - buf = append(buf, ']') - default: - return nil, errors.Errorf("unknown upper bound type %v", src.UpperType) - } - - return buf, nil -} - -func (src Int4range) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var rangeType byte - switch src.LowerType { - case Inclusive: - rangeType |= lowerInclusiveMask - case Unbounded: - rangeType |= lowerUnboundedMask - case Exclusive: - case Empty: - return append(buf, emptyMask), nil - default: - return nil, errors.Errorf("unknown LowerType: %v", src.LowerType) - } - - switch src.UpperType { - case Inclusive: - rangeType |= upperInclusiveMask - case Unbounded: - rangeType |= upperUnboundedMask - case Exclusive: - default: - return nil, errors.Errorf("unknown UpperType: %v", src.UpperType) - } - - buf = append(buf, rangeType) - - var err error - - if src.LowerType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Lower.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - if src.UpperType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Upper.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Int4range) Scan(src interface{}) error { - if src == nil { - *dst = Int4range{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src Int4range) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/int4range_test.go b/pgtype/int4range_test.go deleted file mode 100644 index 95d448f0..00000000 --- a/pgtype/int4range_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package pgtype_test - -import ( - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestInt4rangeTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "int4range", []interface{}{ - &pgtype.Int4range{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Status: pgtype.Present}, - &pgtype.Int4range{Lower: pgtype.Int4{Int: 1, Status: pgtype.Present}, Upper: pgtype.Int4{Int: 10, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - &pgtype.Int4range{Lower: pgtype.Int4{Int: -42, Status: pgtype.Present}, Upper: pgtype.Int4{Int: -5, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - &pgtype.Int4range{Lower: pgtype.Int4{Int: 1, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Unbounded, Status: pgtype.Present}, - &pgtype.Int4range{Upper: pgtype.Int4{Int: 1, Status: pgtype.Present}, LowerType: pgtype.Unbounded, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - &pgtype.Int4range{Status: pgtype.Null}, - }) -} - -func TestInt4rangeNormalize(t *testing.T) { - testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{ - { - SQL: "select int4range(1, 10, '(]')", - Value: pgtype.Int4range{Lower: pgtype.Int4{Int: 2, Status: pgtype.Present}, Upper: pgtype.Int4{Int: 11, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - }, - }) -} diff --git a/pgtype/int8.go b/pgtype/int8.go deleted file mode 100644 index 153f1f7d..00000000 --- a/pgtype/int8.go +++ /dev/null @@ -1,199 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "encoding/json" - "math" - "strconv" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Int8 struct { - Int int64 - Status Status -} - -func (dst *Int8) Set(src interface{}) error { - if src == nil { - *dst = Int8{Status: Null} - return nil - } - - switch value := src.(type) { - case int8: - *dst = Int8{Int: int64(value), Status: Present} - case uint8: - *dst = Int8{Int: int64(value), Status: Present} - case int16: - *dst = Int8{Int: int64(value), Status: Present} - case uint16: - *dst = Int8{Int: int64(value), Status: Present} - case int32: - *dst = Int8{Int: int64(value), Status: Present} - case uint32: - *dst = Int8{Int: int64(value), Status: Present} - case int64: - *dst = Int8{Int: int64(value), Status: Present} - case uint64: - if value > math.MaxInt64 { - return errors.Errorf("%d is greater than maximum value for Int8", value) - } - *dst = Int8{Int: int64(value), Status: Present} - case int: - if int64(value) < math.MinInt64 { - return errors.Errorf("%d is greater than maximum value for Int8", value) - } - if int64(value) > math.MaxInt64 { - return errors.Errorf("%d is greater than maximum value for Int8", value) - } - *dst = Int8{Int: int64(value), Status: Present} - case uint: - if uint64(value) > math.MaxInt64 { - return errors.Errorf("%d is greater than maximum value for Int8", value) - } - *dst = Int8{Int: int64(value), Status: Present} - case string: - num, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - *dst = Int8{Int: num, Status: Present} - default: - if originalSrc, ok := underlyingNumberType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Int8", value) - } - - return nil -} - -func (dst *Int8) Get() interface{} { - switch dst.Status { - case Present: - return dst.Int - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Int8) AssignTo(dst interface{}) error { - return int64AssignTo(int64(src.Int), src.Status, dst) -} - -func (dst *Int8) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int8{Status: Null} - return nil - } - - n, err := strconv.ParseInt(string(src), 10, 64) - if err != nil { - return err - } - - *dst = Int8{Int: n, Status: Present} - return nil -} - -func (dst *Int8) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int8{Status: Null} - return nil - } - - if len(src) != 8 { - return errors.Errorf("invalid length for int8: %v", len(src)) - } - - n := int64(binary.BigEndian.Uint64(src)) - - *dst = Int8{Int: n, Status: Present} - return nil -} - -func (src *Int8) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, strconv.FormatInt(src.Int, 10)...), nil -} - -func (src *Int8) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return pgio.AppendInt64(buf, src.Int), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Int8) Scan(src interface{}) error { - if src == nil { - *dst = Int8{Status: Null} - return nil - } - - switch src := src.(type) { - case int64: - *dst = Int8{Int: src, Status: Present} - return nil - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Int8) Value() (driver.Value, error) { - switch src.Status { - case Present: - return int64(src.Int), nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} - -func (src *Int8) MarshalJSON() ([]byte, error) { - switch src.Status { - case Present: - return []byte(strconv.FormatInt(src.Int, 10)), nil - case Null: - return []byte("null"), nil - case Undefined: - return nil, errUndefined - } - - return nil, errBadStatus -} - -func (dst *Int8) UnmarshalJSON(b []byte) error { - var n int64 - err := json.Unmarshal(b, &n) - if err != nil { - return err - } - - *dst = Int8{Int: n, Status: Present} - - return nil -} diff --git a/pgtype/int8_array.go b/pgtype/int8_array.go deleted file mode 100644 index 392fd47e..00000000 --- a/pgtype/int8_array.go +++ /dev/null @@ -1,329 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Int8Array struct { - Elements []Int8 - Dimensions []ArrayDimension - Status Status -} - -func (dst *Int8Array) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = Int8Array{Status: Null} - return nil - } - - switch value := src.(type) { - - case []int64: - if value == nil { - *dst = Int8Array{Status: Null} - } else if len(value) == 0 { - *dst = Int8Array{Status: Present} - } else { - elements := make([]Int8, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = Int8Array{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - case []uint64: - if value == nil { - *dst = Int8Array{Status: Null} - } else if len(value) == 0 { - *dst = Int8Array{Status: Present} - } else { - elements := make([]Int8, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = Int8Array{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Int8Array", value) - } - - return nil -} - -func (dst *Int8Array) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Int8Array) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]int64: - *v = make([]int64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint64: - *v = make([]uint64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Int8Array) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int8Array{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Int8 - - if len(uta.Elements) > 0 { - elements = make([]Int8, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Int8 - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = Int8Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *Int8Array) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int8Array{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = Int8Array{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Int8, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = Int8Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *Int8Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *Int8Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("int8"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "int8") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Int8Array) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Int8Array) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/int8_array_test.go b/pgtype/int8_array_test.go deleted file mode 100644 index c2d914ab..00000000 --- a/pgtype/int8_array_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestInt8ArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "int8[]", []interface{}{ - &pgtype.Int8Array{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.Int8Array{ - Elements: []pgtype.Int8{ - {Int: 1, Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.Int8Array{Status: pgtype.Null}, - &pgtype.Int8Array{ - Elements: []pgtype.Int8{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Int: 6, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.Int8Array{ - Elements: []pgtype.Int8{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestInt8ArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Int8Array - }{ - { - source: []int64{1}, - result: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: []uint64{1}, - result: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]int64)(nil)), - result: pgtype.Int8Array{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.Int8Array - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestInt8ArrayAssignTo(t *testing.T) { - var int64Slice []int64 - var uint64Slice []uint64 - var namedInt64Slice _int64Slice - - simpleTests := []struct { - src pgtype.Int8Array - dst interface{} - expected interface{} - }{ - { - src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &int64Slice, - expected: []int64{1}, - }, - { - src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &uint64Slice, - expected: []uint64{1}, - }, - { - src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &namedInt64Slice, - expected: _int64Slice{1}, - }, - { - src: pgtype.Int8Array{Status: pgtype.Null}, - dst: &int64Slice, - expected: (([]int64)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Int8Array - dst interface{} - }{ - { - src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &int64Slice, - }, - { - src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: -1, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &uint64Slice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } - -} diff --git a/pgtype/int8_test.go b/pgtype/int8_test.go deleted file mode 100644 index a5f80f42..00000000 --- a/pgtype/int8_test.go +++ /dev/null @@ -1,144 +0,0 @@ -package pgtype_test - -import ( - "math" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestInt8Transcode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "int8", []interface{}{ - &pgtype.Int8{Int: math.MinInt64, Status: pgtype.Present}, - &pgtype.Int8{Int: -1, Status: pgtype.Present}, - &pgtype.Int8{Int: 0, Status: pgtype.Present}, - &pgtype.Int8{Int: 1, Status: pgtype.Present}, - &pgtype.Int8{Int: math.MaxInt64, Status: pgtype.Present}, - &pgtype.Int8{Int: 0, Status: pgtype.Null}, - }) -} - -func TestInt8Set(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Int8 - }{ - {source: int8(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: int16(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: int32(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: int64(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: int8(-1), result: pgtype.Int8{Int: -1, Status: pgtype.Present}}, - {source: int16(-1), result: pgtype.Int8{Int: -1, Status: pgtype.Present}}, - {source: int32(-1), result: pgtype.Int8{Int: -1, Status: pgtype.Present}}, - {source: int64(-1), result: pgtype.Int8{Int: -1, Status: pgtype.Present}}, - {source: uint8(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: uint16(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: uint32(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: uint64(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: "1", result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: _int8(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var r pgtype.Int8 - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestInt8AssignTo(t *testing.T) { - var i8 int8 - var i16 int16 - var i32 int32 - var i64 int64 - var i int - var ui8 uint8 - var ui16 uint16 - var ui32 uint32 - var ui64 uint64 - var ui uint - var pi8 *int8 - var _i8 _int8 - var _pi8 *_int8 - - simpleTests := []struct { - src pgtype.Int8 - dst interface{} - expected interface{} - }{ - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i8, expected: int8(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: pgtype.Int8{Int: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: pgtype.Int8{Int: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.Int8 - dst interface{} - expected interface{} - }{ - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &pi8, expected: int8(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &_pi8, expected: _int8(42)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Int8 - dst interface{} - }{ - {src: pgtype.Int8{Int: 150, Status: pgtype.Present}, dst: &i8}, - {src: pgtype.Int8{Int: 40000, Status: pgtype.Present}, dst: &i16}, - {src: pgtype.Int8{Int: 5000000000, Status: pgtype.Present}, dst: &i32}, - {src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui8}, - {src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui16}, - {src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui32}, - {src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui64}, - {src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui}, - {src: pgtype.Int8{Int: 0, Status: pgtype.Null}, dst: &i64}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/int8range.go b/pgtype/int8range.go deleted file mode 100644 index 0e0f1cdb..00000000 --- a/pgtype/int8range.go +++ /dev/null @@ -1,250 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Int8range struct { - Lower Int8 - Upper Int8 - LowerType BoundType - UpperType BoundType - Status Status -} - -func (dst *Int8range) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Int8range", src) -} - -func (dst *Int8range) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Int8range) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Int8range) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int8range{Status: Null} - return nil - } - - utr, err := ParseUntypedTextRange(string(src)) - if err != nil { - return err - } - - *dst = Int8range{Status: Present} - - dst.LowerType = utr.LowerType - dst.UpperType = utr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeText(ci, []byte(utr.Lower)); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeText(ci, []byte(utr.Upper)); err != nil { - return err - } - } - - return nil -} - -func (dst *Int8range) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Int8range{Status: Null} - return nil - } - - ubr, err := ParseUntypedBinaryRange(src) - if err != nil { - return err - } - - *dst = Int8range{Status: Present} - - dst.LowerType = ubr.LowerType - dst.UpperType = ubr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeBinary(ci, ubr.Lower); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeBinary(ci, ubr.Upper); err != nil { - return err - } - } - - return nil -} - -func (src Int8range) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - switch src.LowerType { - case Exclusive, Unbounded: - buf = append(buf, '(') - case Inclusive: - buf = append(buf, '[') - case Empty: - return append(buf, "empty"...), nil - default: - return nil, errors.Errorf("unknown lower bound type %v", src.LowerType) - } - - var err error - - if src.LowerType != Unbounded { - buf, err = src.Lower.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - } - - buf = append(buf, ',') - - if src.UpperType != Unbounded { - buf, err = src.Upper.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - } - - switch src.UpperType { - case Exclusive, Unbounded: - buf = append(buf, ')') - case Inclusive: - buf = append(buf, ']') - default: - return nil, errors.Errorf("unknown upper bound type %v", src.UpperType) - } - - return buf, nil -} - -func (src Int8range) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var rangeType byte - switch src.LowerType { - case Inclusive: - rangeType |= lowerInclusiveMask - case Unbounded: - rangeType |= lowerUnboundedMask - case Exclusive: - case Empty: - return append(buf, emptyMask), nil - default: - return nil, errors.Errorf("unknown LowerType: %v", src.LowerType) - } - - switch src.UpperType { - case Inclusive: - rangeType |= upperInclusiveMask - case Unbounded: - rangeType |= upperUnboundedMask - case Exclusive: - default: - return nil, errors.Errorf("unknown UpperType: %v", src.UpperType) - } - - buf = append(buf, rangeType) - - var err error - - if src.LowerType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Lower.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - if src.UpperType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Upper.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Int8range) Scan(src interface{}) error { - if src == nil { - *dst = Int8range{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src Int8range) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/int8range_test.go b/pgtype/int8range_test.go deleted file mode 100644 index 01af48bb..00000000 --- a/pgtype/int8range_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package pgtype_test - -import ( - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestInt8rangeTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "Int8range", []interface{}{ - &pgtype.Int8range{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Status: pgtype.Present}, - &pgtype.Int8range{Lower: pgtype.Int8{Int: 1, Status: pgtype.Present}, Upper: pgtype.Int8{Int: 10, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - &pgtype.Int8range{Lower: pgtype.Int8{Int: -42, Status: pgtype.Present}, Upper: pgtype.Int8{Int: -5, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - &pgtype.Int8range{Lower: pgtype.Int8{Int: 1, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Unbounded, Status: pgtype.Present}, - &pgtype.Int8range{Upper: pgtype.Int8{Int: 1, Status: pgtype.Present}, LowerType: pgtype.Unbounded, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - &pgtype.Int8range{Status: pgtype.Null}, - }) -} - -func TestInt8rangeNormalize(t *testing.T) { - testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{ - { - SQL: "select Int8range(1, 10, '(]')", - Value: pgtype.Int8range{Lower: pgtype.Int8{Int: 2, Status: pgtype.Present}, Upper: pgtype.Int8{Int: 11, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - }, - }) -} diff --git a/pgtype/interval.go b/pgtype/interval.go deleted file mode 100644 index a7edca83..00000000 --- a/pgtype/interval.go +++ /dev/null @@ -1,251 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "fmt" - "strconv" - "strings" - "time" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -const ( - microsecondsPerSecond = 1000000 - microsecondsPerMinute = 60 * microsecondsPerSecond - microsecondsPerHour = 60 * microsecondsPerMinute -) - -type Interval struct { - Microseconds int64 - Days int32 - Months int32 - Status Status -} - -func (dst *Interval) Set(src interface{}) error { - if src == nil { - *dst = Interval{Status: Null} - return nil - } - - switch value := src.(type) { - case time.Duration: - *dst = Interval{Microseconds: int64(value) / 1000, Status: Present} - default: - if originalSrc, ok := underlyingPtrType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Interval", value) - } - - return nil -} - -func (dst *Interval) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Interval) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *time.Duration: - if src.Days > 0 || src.Months > 0 { - return errors.Errorf("interval with months or days cannot be decoded into %T", dst) - } - *v = time.Duration(src.Microseconds) * time.Microsecond - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Interval) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Interval{Status: Null} - return nil - } - - var microseconds int64 - var days int32 - var months int32 - - parts := strings.Split(string(src), " ") - - for i := 0; i < len(parts)-1; i += 2 { - scalar, err := strconv.ParseInt(parts[i], 10, 64) - if err != nil { - return errors.Errorf("bad interval format") - } - - switch parts[i+1] { - case "year", "years": - months += int32(scalar * 12) - case "mon", "mons": - months += int32(scalar) - case "day", "days": - days = int32(scalar) - } - } - - if len(parts)%2 == 1 { - timeParts := strings.SplitN(parts[len(parts)-1], ":", 3) - if len(timeParts) != 3 { - return errors.Errorf("bad interval format") - } - - var negative bool - if timeParts[0][0] == '-' { - negative = true - timeParts[0] = timeParts[0][1:] - } - - hours, err := strconv.ParseInt(timeParts[0], 10, 64) - if err != nil { - return errors.Errorf("bad interval hour format: %s", timeParts[0]) - } - - minutes, err := strconv.ParseInt(timeParts[1], 10, 64) - if err != nil { - return errors.Errorf("bad interval minute format: %s", timeParts[1]) - } - - secondParts := strings.SplitN(timeParts[2], ".", 2) - - seconds, err := strconv.ParseInt(secondParts[0], 10, 64) - if err != nil { - return errors.Errorf("bad interval second format: %s", secondParts[0]) - } - - var uSeconds int64 - if len(secondParts) == 2 { - uSeconds, err = strconv.ParseInt(secondParts[1], 10, 64) - if err != nil { - return errors.Errorf("bad interval decimal format: %s", secondParts[1]) - } - - for i := 0; i < 6-len(secondParts[1]); i++ { - uSeconds *= 10 - } - } - - microseconds = hours * microsecondsPerHour - microseconds += minutes * microsecondsPerMinute - microseconds += seconds * microsecondsPerSecond - microseconds += uSeconds - - if negative { - microseconds = -microseconds - } - } - - *dst = Interval{Months: months, Days: days, Microseconds: microseconds, Status: Present} - return nil -} - -func (dst *Interval) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Interval{Status: Null} - return nil - } - - if len(src) != 16 { - return errors.Errorf("Received an invalid size for a interval: %d", len(src)) - } - - microseconds := int64(binary.BigEndian.Uint64(src)) - days := int32(binary.BigEndian.Uint32(src[8:])) - months := int32(binary.BigEndian.Uint32(src[12:])) - - *dst = Interval{Microseconds: microseconds, Days: days, Months: months, Status: Present} - return nil -} - -func (src *Interval) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if src.Months != 0 { - buf = append(buf, strconv.FormatInt(int64(src.Months), 10)...) - buf = append(buf, " mon "...) - } - - if src.Days != 0 { - buf = append(buf, strconv.FormatInt(int64(src.Days), 10)...) - buf = append(buf, " day "...) - } - - absMicroseconds := src.Microseconds - if absMicroseconds < 0 { - absMicroseconds = -absMicroseconds - buf = append(buf, '-') - } - - hours := absMicroseconds / microsecondsPerHour - minutes := (absMicroseconds % microsecondsPerHour) / microsecondsPerMinute - seconds := (absMicroseconds % microsecondsPerMinute) / microsecondsPerSecond - microseconds := absMicroseconds % microsecondsPerSecond - - timeStr := fmt.Sprintf("%02d:%02d:%02d.%06d", hours, minutes, seconds, microseconds) - return append(buf, timeStr...), nil -} - -// EncodeBinary encodes src into w. -func (src *Interval) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = pgio.AppendInt64(buf, src.Microseconds) - buf = pgio.AppendInt32(buf, src.Days) - return pgio.AppendInt32(buf, src.Months), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Interval) Scan(src interface{}) error { - if src == nil { - *dst = Interval{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Interval) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/interval_test.go b/pgtype/interval_test.go deleted file mode 100644 index 7cafb0ae..00000000 --- a/pgtype/interval_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package pgtype_test - -import ( - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestIntervalTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "interval", []interface{}{ - &pgtype.Interval{Microseconds: 1, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: 1000000, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: 1000001, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: 123202800000000, Status: pgtype.Present}, - &pgtype.Interval{Days: 1, Status: pgtype.Present}, - &pgtype.Interval{Months: 1, Status: pgtype.Present}, - &pgtype.Interval{Months: 12, Status: pgtype.Present}, - &pgtype.Interval{Months: 13, Days: 15, Microseconds: 1000001, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: -1, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: -1000000, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: -1000001, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: -123202800000000, Status: pgtype.Present}, - &pgtype.Interval{Days: -1, Status: pgtype.Present}, - &pgtype.Interval{Months: -1, Status: pgtype.Present}, - &pgtype.Interval{Months: -12, Status: pgtype.Present}, - &pgtype.Interval{Months: -13, Days: -15, Microseconds: -1000001, Status: pgtype.Present}, - &pgtype.Interval{Status: pgtype.Null}, - }) -} - -func TestIntervalNormalize(t *testing.T) { - testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{ - { - SQL: "select '1 second'::interval", - Value: &pgtype.Interval{Microseconds: 1000000, Status: pgtype.Present}, - }, - { - SQL: "select '1.000001 second'::interval", - Value: &pgtype.Interval{Microseconds: 1000001, Status: pgtype.Present}, - }, - { - SQL: "select '34223 hours'::interval", - Value: &pgtype.Interval{Microseconds: 123202800000000, Status: pgtype.Present}, - }, - { - SQL: "select '1 day'::interval", - Value: &pgtype.Interval{Days: 1, Status: pgtype.Present}, - }, - { - SQL: "select '1 month'::interval", - Value: &pgtype.Interval{Months: 1, Status: pgtype.Present}, - }, - { - SQL: "select '1 year'::interval", - Value: &pgtype.Interval{Months: 12, Status: pgtype.Present}, - }, - { - SQL: "select '-13 mon'::interval", - Value: &pgtype.Interval{Months: -13, Status: pgtype.Present}, - }, - }) -} diff --git a/pgtype/json.go b/pgtype/json.go deleted file mode 100644 index 49ff7a6c..00000000 --- a/pgtype/json.go +++ /dev/null @@ -1,167 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/json" - - errors "golang.org/x/xerrors" -) - -type JSON struct { - Bytes []byte - Status Status -} - -func (dst *JSON) Set(src interface{}) error { - if src == nil { - *dst = JSON{Status: Null} - return nil - } - - switch value := src.(type) { - case string: - *dst = JSON{Bytes: []byte(value), Status: Present} - case *string: - if value == nil { - *dst = JSON{Status: Null} - } else { - *dst = JSON{Bytes: []byte(*value), Status: Present} - } - case []byte: - if value == nil { - *dst = JSON{Status: Null} - } else { - *dst = JSON{Bytes: value, Status: Present} - } - // Encode* methods are defined on *JSON. If JSON is passed directly then the - // struct itself would be encoded instead of Bytes. This is clearly a footgun - // so detect and return an error. See https://github.com/jackc/pgx/issues/350. - case JSON: - return errors.New("use pointer to pgtype.JSON instead of value") - // Same as above but for JSONB (because they share implementation) - case JSONB: - return errors.New("use pointer to pgtype.JSONB instead of value") - - default: - buf, err := json.Marshal(value) - if err != nil { - return err - } - *dst = JSON{Bytes: buf, Status: Present} - } - - return nil -} - -func (dst *JSON) Get() interface{} { - switch dst.Status { - case Present: - var i interface{} - err := json.Unmarshal(dst.Bytes, &i) - if err != nil { - return dst - } - return i - case Null: - return nil - default: - return dst.Status - } -} - -func (src *JSON) AssignTo(dst interface{}) error { - switch v := dst.(type) { - case *string: - if src.Status == Present { - *v = string(src.Bytes) - } else { - return errors.Errorf("cannot assign non-present status to %T", dst) - } - case **string: - if src.Status == Present { - s := string(src.Bytes) - *v = &s - return nil - } else { - *v = nil - return nil - } - case *[]byte: - if src.Status != Present { - *v = nil - } else { - buf := make([]byte, len(src.Bytes)) - copy(buf, src.Bytes) - *v = buf - } - default: - data := src.Bytes - if data == nil || src.Status != Present { - data = []byte("null") - } - - return json.Unmarshal(data, dst) - } - - return nil -} - -func (dst *JSON) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = JSON{Status: Null} - return nil - } - - *dst = JSON{Bytes: src, Status: Present} - return nil -} - -func (dst *JSON) DecodeBinary(ci *ConnInfo, src []byte) error { - return dst.DecodeText(ci, src) -} - -func (src *JSON) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, src.Bytes...), nil -} - -func (src *JSON) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - return src.EncodeText(ci, buf) -} - -// Scan implements the database/sql Scanner interface. -func (dst *JSON) Scan(src interface{}) error { - if src == nil { - *dst = JSON{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *JSON) Value() (driver.Value, error) { - switch src.Status { - case Present: - return src.Bytes, nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} diff --git a/pgtype/json_test.go b/pgtype/json_test.go deleted file mode 100644 index bb0f1b20..00000000 --- a/pgtype/json_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package pgtype_test - -import ( - "bytes" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestJSONTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "json", []interface{}{ - &pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}, - &pgtype.JSON{Bytes: []byte("null"), Status: pgtype.Present}, - &pgtype.JSON{Bytes: []byte("42"), Status: pgtype.Present}, - &pgtype.JSON{Bytes: []byte(`"hello"`), Status: pgtype.Present}, - &pgtype.JSON{Status: pgtype.Null}, - }) -} - -func TestJSONSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.JSON - }{ - {source: "{}", result: pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}}, - {source: []byte("{}"), result: pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}}, - {source: ([]byte)(nil), result: pgtype.JSON{Status: pgtype.Null}}, - {source: (*string)(nil), result: pgtype.JSON{Status: pgtype.Null}}, - {source: []int{1, 2, 3}, result: pgtype.JSON{Bytes: []byte("[1,2,3]"), Status: pgtype.Present}}, - {source: map[string]interface{}{"foo": "bar"}, result: pgtype.JSON{Bytes: []byte(`{"foo":"bar"}`), Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var d pgtype.JSON - err := d.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(d, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, d) - } - } -} - -func TestJSONAssignTo(t *testing.T) { - var s string - var ps *string - var b []byte - - rawStringTests := []struct { - src pgtype.JSON - dst *string - expected string - }{ - {src: pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}, dst: &s, expected: "{}"}, - } - - for i, tt := range rawStringTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if *tt.dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst) - } - } - - rawBytesTests := []struct { - src pgtype.JSON - dst *[]byte - expected []byte - }{ - {src: pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}, dst: &b, expected: []byte("{}")}, - {src: pgtype.JSON{Status: pgtype.Null}, dst: &b, expected: (([]byte)(nil))}, - } - - for i, tt := range rawBytesTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if bytes.Compare(tt.expected, *tt.dst) != 0 { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst) - } - } - - var mapDst map[string]interface{} - type structDst struct { - Name string `json:"name"` - Age int `json:"age"` - } - var strDst structDst - - unmarshalTests := []struct { - src pgtype.JSON - dst interface{} - expected interface{} - }{ - {src: pgtype.JSON{Bytes: []byte(`{"foo":"bar"}`), Status: pgtype.Present}, dst: &mapDst, expected: map[string]interface{}{"foo": "bar"}}, - {src: pgtype.JSON{Bytes: []byte(`{"name":"John","age":42}`), Status: pgtype.Present}, dst: &strDst, expected: structDst{Name: "John", Age: 42}}, - } - for i, tt := range unmarshalTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.JSON - dst **string - expected *string - }{ - {src: pgtype.JSON{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if *tt.dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst) - } - } -} diff --git a/pgtype/jsonb.go b/pgtype/jsonb.go deleted file mode 100644 index 065e4e21..00000000 --- a/pgtype/jsonb.go +++ /dev/null @@ -1,70 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - - errors "golang.org/x/xerrors" -) - -type JSONB JSON - -func (dst *JSONB) Set(src interface{}) error { - return (*JSON)(dst).Set(src) -} - -func (dst *JSONB) Get() interface{} { - return (*JSON)(dst).Get() -} - -func (src *JSONB) AssignTo(dst interface{}) error { - return (*JSON)(src).AssignTo(dst) -} - -func (dst *JSONB) DecodeText(ci *ConnInfo, src []byte) error { - return (*JSON)(dst).DecodeText(ci, src) -} - -func (dst *JSONB) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = JSONB{Status: Null} - return nil - } - - if len(src) == 0 { - return errors.Errorf("jsonb too short") - } - - if src[0] != 1 { - return errors.Errorf("unknown jsonb version number %d", src[0]) - } - - *dst = JSONB{Bytes: src[1:], Status: Present} - return nil - -} - -func (src *JSONB) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*JSON)(src).EncodeText(ci, buf) -} - -func (src *JSONB) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = append(buf, 1) - return append(buf, src.Bytes...), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *JSONB) Scan(src interface{}) error { - return (*JSON)(dst).Scan(src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *JSONB) Value() (driver.Value, error) { - return (*JSON)(src).Value() -} diff --git a/pgtype/jsonb_test.go b/pgtype/jsonb_test.go deleted file mode 100644 index 73656c76..00000000 --- a/pgtype/jsonb_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package pgtype_test - -import ( - "bytes" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestJSONBTranscode(t *testing.T) { - conn := testutil.MustConnectPgx(t) - defer testutil.MustCloseContext(t, conn) - if _, ok := conn.ConnInfo.DataTypeForName("jsonb"); !ok { - t.Skip("Skipping due to no jsonb type") - } - - testutil.TestSuccessfulTranscode(t, "jsonb", []interface{}{ - &pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}, - &pgtype.JSONB{Bytes: []byte("null"), Status: pgtype.Present}, - &pgtype.JSONB{Bytes: []byte("42"), Status: pgtype.Present}, - &pgtype.JSONB{Bytes: []byte(`"hello"`), Status: pgtype.Present}, - &pgtype.JSONB{Status: pgtype.Null}, - }) -} - -func TestJSONBSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.JSONB - }{ - {source: "{}", result: pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}}, - {source: []byte("{}"), result: pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}}, - {source: ([]byte)(nil), result: pgtype.JSONB{Status: pgtype.Null}}, - {source: (*string)(nil), result: pgtype.JSONB{Status: pgtype.Null}}, - {source: []int{1, 2, 3}, result: pgtype.JSONB{Bytes: []byte("[1,2,3]"), Status: pgtype.Present}}, - {source: map[string]interface{}{"foo": "bar"}, result: pgtype.JSONB{Bytes: []byte(`{"foo":"bar"}`), Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var d pgtype.JSONB - err := d.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(d, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, d) - } - } -} - -func TestJSONBAssignTo(t *testing.T) { - var s string - var ps *string - var b []byte - - rawStringTests := []struct { - src pgtype.JSONB - dst *string - expected string - }{ - {src: pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}, dst: &s, expected: "{}"}, - } - - for i, tt := range rawStringTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if *tt.dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst) - } - } - - rawBytesTests := []struct { - src pgtype.JSONB - dst *[]byte - expected []byte - }{ - {src: pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}, dst: &b, expected: []byte("{}")}, - {src: pgtype.JSONB{Status: pgtype.Null}, dst: &b, expected: (([]byte)(nil))}, - } - - for i, tt := range rawBytesTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if bytes.Compare(tt.expected, *tt.dst) != 0 { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst) - } - } - - var mapDst map[string]interface{} - type structDst struct { - Name string `json:"name"` - Age int `json:"age"` - } - var strDst structDst - - unmarshalTests := []struct { - src pgtype.JSONB - dst interface{} - expected interface{} - }{ - {src: pgtype.JSONB{Bytes: []byte(`{"foo":"bar"}`), Status: pgtype.Present}, dst: &mapDst, expected: map[string]interface{}{"foo": "bar"}}, - {src: pgtype.JSONB{Bytes: []byte(`{"name":"John","age":42}`), Status: pgtype.Present}, dst: &strDst, expected: structDst{Name: "John", Age: 42}}, - } - for i, tt := range unmarshalTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.JSONB - dst **string - expected *string - }{ - {src: pgtype.JSONB{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if *tt.dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst) - } - } -} diff --git a/pgtype/line.go b/pgtype/line.go deleted file mode 100644 index 617ee456..00000000 --- a/pgtype/line.go +++ /dev/null @@ -1,149 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "fmt" - "math" - "strconv" - "strings" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Line struct { - A, B, C float64 - Status Status -} - -func (dst *Line) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Line", src) -} - -func (dst *Line) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Line) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Line) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Line{Status: Null} - return nil - } - - if len(src) < 7 { - return errors.Errorf("invalid length for Line: %v", len(src)) - } - - parts := strings.SplitN(string(src[1:len(src)-1]), ",", 3) - if len(parts) < 3 { - return errors.Errorf("invalid format for line") - } - - a, err := strconv.ParseFloat(parts[0], 64) - if err != nil { - return err - } - - b, err := strconv.ParseFloat(parts[1], 64) - if err != nil { - return err - } - - c, err := strconv.ParseFloat(parts[2], 64) - if err != nil { - return err - } - - *dst = Line{A: a, B: b, C: c, Status: Present} - return nil -} - -func (dst *Line) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Line{Status: Null} - return nil - } - - if len(src) != 24 { - return errors.Errorf("invalid length for Line: %v", len(src)) - } - - a := binary.BigEndian.Uint64(src) - b := binary.BigEndian.Uint64(src[8:]) - c := binary.BigEndian.Uint64(src[16:]) - - *dst = Line{ - A: math.Float64frombits(a), - B: math.Float64frombits(b), - C: math.Float64frombits(c), - Status: Present, - } - return nil -} - -func (src *Line) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = append(buf, fmt.Sprintf(`{%s,%s,%s}`, - strconv.FormatFloat(src.A, 'f', -1, 64), - strconv.FormatFloat(src.B, 'f', -1, 64), - strconv.FormatFloat(src.C, 'f', -1, 64), - )...) - - return buf, nil -} - -func (src *Line) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = pgio.AppendUint64(buf, math.Float64bits(src.A)) - buf = pgio.AppendUint64(buf, math.Float64bits(src.B)) - buf = pgio.AppendUint64(buf, math.Float64bits(src.C)) - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Line) Scan(src interface{}) error { - if src == nil { - *dst = Line{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Line) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/line_test.go b/pgtype/line_test.go deleted file mode 100644 index 5f0a58a3..00000000 --- a/pgtype/line_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package pgtype_test - -import ( - "context" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestLineTranscode(t *testing.T) { - conn := testutil.MustConnectPgx(t) - if _, ok := conn.ConnInfo.DataTypeForName("line"); !ok { - t.Skip("Skipping due to no line type") - } - - // line may exist but not be usable on 9.3 :( - var isPG93 bool - err := conn.QueryRow(context.Background(), "select version() ~ '9.3'").Scan(&isPG93) - if err != nil { - t.Fatal(err) - } - if isPG93 { - t.Skip("Skipping due to unimplemented line type in PG 9.3") - } - - testutil.TestSuccessfulTranscode(t, "line", []interface{}{ - &pgtype.Line{ - A: 1.23, B: 4.56, C: 7.89012345, - Status: pgtype.Present, - }, - &pgtype.Line{ - A: -1.23, B: -4.56, C: -7.89, - Status: pgtype.Present, - }, - &pgtype.Line{Status: pgtype.Null}, - }) -} diff --git a/pgtype/lseg.go b/pgtype/lseg.go deleted file mode 100644 index b8d6e322..00000000 --- a/pgtype/lseg.go +++ /dev/null @@ -1,166 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "fmt" - "math" - "strconv" - "strings" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Lseg struct { - P [2]Vec2 - Status Status -} - -func (dst *Lseg) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Lseg", src) -} - -func (dst *Lseg) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Lseg) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Lseg) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Lseg{Status: Null} - return nil - } - - if len(src) < 11 { - return errors.Errorf("invalid length for Lseg: %v", len(src)) - } - - str := string(src[2:]) - - var end int - end = strings.IndexByte(str, ',') - - x1, err := strconv.ParseFloat(str[:end], 64) - if err != nil { - return err - } - - str = str[end+1:] - end = strings.IndexByte(str, ')') - - y1, err := strconv.ParseFloat(str[:end], 64) - if err != nil { - return err - } - - str = str[end+3:] - end = strings.IndexByte(str, ',') - - x2, err := strconv.ParseFloat(str[:end], 64) - if err != nil { - return err - } - - str = str[end+1 : len(str)-2] - - y2, err := strconv.ParseFloat(str, 64) - if err != nil { - return err - } - - *dst = Lseg{P: [2]Vec2{{x1, y1}, {x2, y2}}, Status: Present} - return nil -} - -func (dst *Lseg) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Lseg{Status: Null} - return nil - } - - if len(src) != 32 { - return errors.Errorf("invalid length for Lseg: %v", len(src)) - } - - x1 := binary.BigEndian.Uint64(src) - y1 := binary.BigEndian.Uint64(src[8:]) - x2 := binary.BigEndian.Uint64(src[16:]) - y2 := binary.BigEndian.Uint64(src[24:]) - - *dst = Lseg{ - P: [2]Vec2{ - {math.Float64frombits(x1), math.Float64frombits(y1)}, - {math.Float64frombits(x2), math.Float64frombits(y2)}, - }, - Status: Present, - } - return nil -} - -func (src *Lseg) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = append(buf, fmt.Sprintf(`(%s,%s),(%s,%s)`, - strconv.FormatFloat(src.P[0].X, 'f', -1, 64), - strconv.FormatFloat(src.P[0].Y, 'f', -1, 64), - strconv.FormatFloat(src.P[1].X, 'f', -1, 64), - strconv.FormatFloat(src.P[1].Y, 'f', -1, 64), - )...) - - return buf, nil -} - -func (src *Lseg) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = pgio.AppendUint64(buf, math.Float64bits(src.P[0].X)) - buf = pgio.AppendUint64(buf, math.Float64bits(src.P[0].Y)) - buf = pgio.AppendUint64(buf, math.Float64bits(src.P[1].X)) - buf = pgio.AppendUint64(buf, math.Float64bits(src.P[1].Y)) - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Lseg) Scan(src interface{}) error { - if src == nil { - *dst = Lseg{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Lseg) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/lseg_test.go b/pgtype/lseg_test.go deleted file mode 100644 index 100bdf0f..00000000 --- a/pgtype/lseg_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package pgtype_test - -import ( - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestLsegTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "lseg", []interface{}{ - &pgtype.Lseg{ - P: [2]pgtype.Vec2{{3.14, 1.678}, {7.1, 5.2345678901}}, - Status: pgtype.Present, - }, - &pgtype.Lseg{ - P: [2]pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}}, - Status: pgtype.Present, - }, - &pgtype.Lseg{Status: pgtype.Null}, - }) -} diff --git a/pgtype/macaddr.go b/pgtype/macaddr.go deleted file mode 100644 index 25ffc48e..00000000 --- a/pgtype/macaddr.go +++ /dev/null @@ -1,155 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "net" - - errors "golang.org/x/xerrors" -) - -type Macaddr struct { - Addr net.HardwareAddr - Status Status -} - -func (dst *Macaddr) Set(src interface{}) error { - if src == nil { - *dst = Macaddr{Status: Null} - return nil - } - - switch value := src.(type) { - case net.HardwareAddr: - addr := make(net.HardwareAddr, len(value)) - copy(addr, value) - *dst = Macaddr{Addr: addr, Status: Present} - case string: - addr, err := net.ParseMAC(value) - if err != nil { - return err - } - *dst = Macaddr{Addr: addr, Status: Present} - default: - if originalSrc, ok := underlyingPtrType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Macaddr", value) - } - - return nil -} - -func (dst *Macaddr) Get() interface{} { - switch dst.Status { - case Present: - return dst.Addr - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Macaddr) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *net.HardwareAddr: - *v = make(net.HardwareAddr, len(src.Addr)) - copy(*v, src.Addr) - return nil - case *string: - *v = src.Addr.String() - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Macaddr) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Macaddr{Status: Null} - return nil - } - - addr, err := net.ParseMAC(string(src)) - if err != nil { - return err - } - - *dst = Macaddr{Addr: addr, Status: Present} - return nil -} - -func (dst *Macaddr) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Macaddr{Status: Null} - return nil - } - - if len(src) != 6 { - return errors.Errorf("Received an invalid size for a macaddr: %d", len(src)) - } - - addr := make(net.HardwareAddr, 6) - copy(addr, src) - - *dst = Macaddr{Addr: addr, Status: Present} - - return nil -} - -func (src *Macaddr) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, src.Addr.String()...), nil -} - -// EncodeBinary encodes src into w. -func (src *Macaddr) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, src.Addr...), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Macaddr) Scan(src interface{}) error { - if src == nil { - *dst = Macaddr{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Macaddr) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/macaddr_array.go b/pgtype/macaddr_array.go deleted file mode 100644 index 0b791104..00000000 --- a/pgtype/macaddr_array.go +++ /dev/null @@ -1,302 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "net" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type MacaddrArray struct { - Elements []Macaddr - Dimensions []ArrayDimension - Status Status -} - -func (dst *MacaddrArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = MacaddrArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []net.HardwareAddr: - if value == nil { - *dst = MacaddrArray{Status: Null} - } else if len(value) == 0 { - *dst = MacaddrArray{Status: Present} - } else { - elements := make([]Macaddr, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = MacaddrArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to MacaddrArray", value) - } - - return nil -} - -func (dst *MacaddrArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *MacaddrArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]net.HardwareAddr: - *v = make([]net.HardwareAddr, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *MacaddrArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = MacaddrArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Macaddr - - if len(uta.Elements) > 0 { - elements = make([]Macaddr, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Macaddr - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = MacaddrArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *MacaddrArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = MacaddrArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = MacaddrArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Macaddr, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = MacaddrArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *MacaddrArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *MacaddrArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("macaddr"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "macaddr") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *MacaddrArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *MacaddrArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/macaddr_array_test.go b/pgtype/macaddr_array_test.go deleted file mode 100644 index cf07ebf6..00000000 --- a/pgtype/macaddr_array_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package pgtype_test - -import ( - "net" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestMacaddrArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "macaddr[]", []interface{}{ - &pgtype.MacaddrArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.MacaddrArray{ - Elements: []pgtype.Macaddr{ - {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.MacaddrArray{Status: pgtype.Null}, - }) -} - -func TestMacaddrArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.MacaddrArray - }{ - { - source: []net.HardwareAddr{mustParseMacaddr(t, "01:23:45:67:89:ab")}, - result: pgtype.MacaddrArray{ - Elements: []pgtype.Macaddr{{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]net.HardwareAddr)(nil)), - result: pgtype.MacaddrArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.MacaddrArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestMacaddrArrayAssignTo(t *testing.T) { - var macaddrSlice []net.HardwareAddr - - simpleTests := []struct { - src pgtype.MacaddrArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.MacaddrArray{ - Elements: []pgtype.Macaddr{{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &macaddrSlice, - expected: []net.HardwareAddr{mustParseMacaddr(t, "01:23:45:67:89:ab")}, - }, - { - src: pgtype.MacaddrArray{ - Elements: []pgtype.Macaddr{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &macaddrSlice, - expected: []net.HardwareAddr{nil}, - }, - { - src: pgtype.MacaddrArray{Status: pgtype.Null}, - dst: &macaddrSlice, - expected: (([]net.HardwareAddr)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } -} diff --git a/pgtype/macaddr_test.go b/pgtype/macaddr_test.go deleted file mode 100644 index a08671c0..00000000 --- a/pgtype/macaddr_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package pgtype_test - -import ( - "bytes" - "net" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestMacaddrTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "macaddr", []interface{}{ - &pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - &pgtype.Macaddr{Status: pgtype.Null}, - }) -} - -func TestMacaddrSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Macaddr - }{ - { - source: mustParseMacaddr(t, "01:23:45:67:89:ab"), - result: pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - }, - { - source: "01:23:45:67:89:ab", - result: pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.Macaddr - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestMacaddrAssignTo(t *testing.T) { - { - src := pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present} - var dst net.HardwareAddr - expected := mustParseMacaddr(t, "01:23:45:67:89:ab") - - err := src.AssignTo(&dst) - if err != nil { - t.Error(err) - } - - if bytes.Compare([]byte(dst), []byte(expected)) != 0 { - t.Errorf("expected %v to assign %v, but result was %v", src, expected, dst) - } - } - - { - src := pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present} - var dst string - expected := "01:23:45:67:89:ab" - - err := src.AssignTo(&dst) - if err != nil { - t.Error(err) - } - - if dst != expected { - t.Errorf("expected %v to assign %v, but result was %v", src, expected, dst) - } - } -} diff --git a/pgtype/name.go b/pgtype/name.go deleted file mode 100644 index af064a82..00000000 --- a/pgtype/name.go +++ /dev/null @@ -1,58 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" -) - -// Name is a type used for PostgreSQL's special 63-byte -// name data type, used for identifiers like table names. -// The pg_class.relname column is a good example of where the -// name data type is used. -// -// Note that the underlying Go data type of pgx.Name is string, -// so there is no way to enforce the 63-byte length. Inputting -// a longer name into PostgreSQL will result in silent truncation -// to 63 bytes. -// -// Also, if you have custom-compiled PostgreSQL and set -// NAMEDATALEN to a different value, obviously that number of -// bytes applies, rather than the default 63. -type Name Text - -func (dst *Name) Set(src interface{}) error { - return (*Text)(dst).Set(src) -} - -func (dst *Name) Get() interface{} { - return (*Text)(dst).Get() -} - -func (src *Name) AssignTo(dst interface{}) error { - return (*Text)(src).AssignTo(dst) -} - -func (dst *Name) DecodeText(ci *ConnInfo, src []byte) error { - return (*Text)(dst).DecodeText(ci, src) -} - -func (dst *Name) DecodeBinary(ci *ConnInfo, src []byte) error { - return (*Text)(dst).DecodeBinary(ci, src) -} - -func (src *Name) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*Text)(src).EncodeText(ci, buf) -} - -func (src *Name) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*Text)(src).EncodeBinary(ci, buf) -} - -// Scan implements the database/sql Scanner interface. -func (dst *Name) Scan(src interface{}) error { - return (*Text)(dst).Scan(src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Name) Value() (driver.Value, error) { - return (*Text)(src).Value() -} diff --git a/pgtype/name_test.go b/pgtype/name_test.go deleted file mode 100644 index 75d7b95a..00000000 --- a/pgtype/name_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestNameTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "name", []interface{}{ - &pgtype.Name{String: "", Status: pgtype.Present}, - &pgtype.Name{String: "foo", Status: pgtype.Present}, - &pgtype.Name{Status: pgtype.Null}, - }) -} - -func TestNameSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Name - }{ - {source: "foo", result: pgtype.Name{String: "foo", Status: pgtype.Present}}, - {source: _string("bar"), result: pgtype.Name{String: "bar", Status: pgtype.Present}}, - {source: (*string)(nil), result: pgtype.Name{Status: pgtype.Null}}, - } - - for i, tt := range successfulTests { - var d pgtype.Name - err := d.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if d != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, d) - } - } -} - -func TestNameAssignTo(t *testing.T) { - var s string - var ps *string - - simpleTests := []struct { - src pgtype.Name - dst interface{} - expected interface{} - }{ - {src: pgtype.Name{String: "foo", Status: pgtype.Present}, dst: &s, expected: "foo"}, - {src: pgtype.Name{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.Name - dst interface{} - expected interface{} - }{ - {src: pgtype.Name{String: "foo", Status: pgtype.Present}, dst: &ps, expected: "foo"}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Name - dst interface{} - }{ - {src: pgtype.Name{Status: pgtype.Null}, dst: &s}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/numeric.go b/pgtype/numeric.go deleted file mode 100644 index bbd7667a..00000000 --- a/pgtype/numeric.go +++ /dev/null @@ -1,597 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "math" - "math/big" - "strconv" - "strings" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -// PostgreSQL internal numeric storage uses 16-bit "digits" with base of 10,000 -const nbase = 10000 - -var big0 *big.Int = big.NewInt(0) -var big1 *big.Int = big.NewInt(1) -var big10 *big.Int = big.NewInt(10) -var big100 *big.Int = big.NewInt(100) -var big1000 *big.Int = big.NewInt(1000) - -var bigMaxInt8 *big.Int = big.NewInt(math.MaxInt8) -var bigMinInt8 *big.Int = big.NewInt(math.MinInt8) -var bigMaxInt16 *big.Int = big.NewInt(math.MaxInt16) -var bigMinInt16 *big.Int = big.NewInt(math.MinInt16) -var bigMaxInt32 *big.Int = big.NewInt(math.MaxInt32) -var bigMinInt32 *big.Int = big.NewInt(math.MinInt32) -var bigMaxInt64 *big.Int = big.NewInt(math.MaxInt64) -var bigMinInt64 *big.Int = big.NewInt(math.MinInt64) -var bigMaxInt *big.Int = big.NewInt(int64(maxInt)) -var bigMinInt *big.Int = big.NewInt(int64(minInt)) - -var bigMaxUint8 *big.Int = big.NewInt(math.MaxUint8) -var bigMaxUint16 *big.Int = big.NewInt(math.MaxUint16) -var bigMaxUint32 *big.Int = big.NewInt(math.MaxUint32) -var bigMaxUint64 *big.Int = (&big.Int{}).SetUint64(uint64(math.MaxUint64)) -var bigMaxUint *big.Int = (&big.Int{}).SetUint64(uint64(maxUint)) - -var bigNBase *big.Int = big.NewInt(nbase) -var bigNBaseX2 *big.Int = big.NewInt(nbase * nbase) -var bigNBaseX3 *big.Int = big.NewInt(nbase * nbase * nbase) -var bigNBaseX4 *big.Int = big.NewInt(nbase * nbase * nbase * nbase) - -type Numeric struct { - Int *big.Int - Exp int32 - Status Status -} - -func (dst *Numeric) Set(src interface{}) error { - if src == nil { - *dst = Numeric{Status: Null} - return nil - } - - switch value := src.(type) { - case float32: - num, exp, err := parseNumericString(strconv.FormatFloat(float64(value), 'f', -1, 64)) - if err != nil { - return err - } - *dst = Numeric{Int: num, Exp: exp, Status: Present} - case float64: - num, exp, err := parseNumericString(strconv.FormatFloat(value, 'f', -1, 64)) - if err != nil { - return err - } - *dst = Numeric{Int: num, Exp: exp, Status: Present} - case int8: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} - case uint8: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} - case int16: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} - case uint16: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} - case int32: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} - case uint32: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} - case int64: - *dst = Numeric{Int: big.NewInt(value), Status: Present} - case uint64: - *dst = Numeric{Int: (&big.Int{}).SetUint64(value), Status: Present} - case int: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} - case uint: - *dst = Numeric{Int: (&big.Int{}).SetUint64(uint64(value)), Status: Present} - case string: - num, exp, err := parseNumericString(value) - if err != nil { - return err - } - *dst = Numeric{Int: num, Exp: exp, Status: Present} - default: - if originalSrc, ok := underlyingNumberType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Numeric", value) - } - - return nil -} - -func (dst *Numeric) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Numeric) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *float32: - f, err := src.toFloat64() - if err != nil { - return err - } - return float64AssignTo(f, src.Status, dst) - case *float64: - f, err := src.toFloat64() - if err != nil { - return err - } - return float64AssignTo(f, src.Status, dst) - case *int: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(bigMaxInt) > 0 { - return errors.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) - } - if normalizedInt.Cmp(bigMinInt) < 0 { - return errors.Errorf("%v is less than minimum value for %T", normalizedInt, *v) - } - *v = int(normalizedInt.Int64()) - case *int8: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(bigMaxInt8) > 0 { - return errors.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) - } - if normalizedInt.Cmp(bigMinInt8) < 0 { - return errors.Errorf("%v is less than minimum value for %T", normalizedInt, *v) - } - *v = int8(normalizedInt.Int64()) - case *int16: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(bigMaxInt16) > 0 { - return errors.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) - } - if normalizedInt.Cmp(bigMinInt16) < 0 { - return errors.Errorf("%v is less than minimum value for %T", normalizedInt, *v) - } - *v = int16(normalizedInt.Int64()) - case *int32: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(bigMaxInt32) > 0 { - return errors.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) - } - if normalizedInt.Cmp(bigMinInt32) < 0 { - return errors.Errorf("%v is less than minimum value for %T", normalizedInt, *v) - } - *v = int32(normalizedInt.Int64()) - case *int64: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(bigMaxInt64) > 0 { - return errors.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) - } - if normalizedInt.Cmp(bigMinInt64) < 0 { - return errors.Errorf("%v is less than minimum value for %T", normalizedInt, *v) - } - *v = normalizedInt.Int64() - case *uint: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(big0) < 0 { - return errors.Errorf("%d is less than zero for %T", normalizedInt, *v) - } else if normalizedInt.Cmp(bigMaxUint) > 0 { - return errors.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) - } - *v = uint(normalizedInt.Uint64()) - case *uint8: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(big0) < 0 { - return errors.Errorf("%d is less than zero for %T", normalizedInt, *v) - } else if normalizedInt.Cmp(bigMaxUint8) > 0 { - return errors.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) - } - *v = uint8(normalizedInt.Uint64()) - case *uint16: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(big0) < 0 { - return errors.Errorf("%d is less than zero for %T", normalizedInt, *v) - } else if normalizedInt.Cmp(bigMaxUint16) > 0 { - return errors.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) - } - *v = uint16(normalizedInt.Uint64()) - case *uint32: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(big0) < 0 { - return errors.Errorf("%d is less than zero for %T", normalizedInt, *v) - } else if normalizedInt.Cmp(bigMaxUint32) > 0 { - return errors.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) - } - *v = uint32(normalizedInt.Uint64()) - case *uint64: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(big0) < 0 { - return errors.Errorf("%d is less than zero for %T", normalizedInt, *v) - } else if normalizedInt.Cmp(bigMaxUint64) > 0 { - return errors.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) - } - *v = normalizedInt.Uint64() - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return nil -} - -func (dst *Numeric) toBigInt() (*big.Int, error) { - if dst.Exp == 0 { - return dst.Int, nil - } - - num := &big.Int{} - num.Set(dst.Int) - if dst.Exp > 0 { - mul := &big.Int{} - mul.Exp(big10, big.NewInt(int64(dst.Exp)), nil) - num.Mul(num, mul) - return num, nil - } - - div := &big.Int{} - div.Exp(big10, big.NewInt(int64(-dst.Exp)), nil) - remainder := &big.Int{} - num.DivMod(num, div, remainder) - if remainder.Cmp(big0) != 0 { - return nil, errors.Errorf("cannot convert %v to integer", dst) - } - return num, nil -} - -func (src *Numeric) toFloat64() (float64, error) { - f, err := strconv.ParseFloat(src.Int.String(), 64) - if err != nil { - return 0, err - } - if src.Exp > 0 { - for i := 0; i < int(src.Exp); i++ { - f *= 10 - } - } else if src.Exp < 0 { - for i := 0; i > int(src.Exp); i-- { - f /= 10 - } - } - return f, nil -} - -func (dst *Numeric) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Numeric{Status: Null} - return nil - } - - num, exp, err := parseNumericString(string(src)) - if err != nil { - return err - } - - *dst = Numeric{Int: num, Exp: exp, Status: Present} - return nil -} - -func parseNumericString(str string) (n *big.Int, exp int32, err error) { - parts := strings.SplitN(str, ".", 2) - digits := strings.Join(parts, "") - - if len(parts) > 1 { - exp = int32(-len(parts[1])) - } else { - for len(digits) > 1 && digits[len(digits)-1] == '0' { - digits = digits[:len(digits)-1] - exp++ - } - } - - accum := &big.Int{} - if _, ok := accum.SetString(digits, 10); !ok { - return nil, 0, errors.Errorf("%s is not a number", str) - } - - return accum, exp, nil -} - -func (dst *Numeric) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Numeric{Status: Null} - return nil - } - - if len(src) < 8 { - return errors.Errorf("numeric incomplete %v", src) - } - - rp := 0 - ndigits := int16(binary.BigEndian.Uint16(src[rp:])) - rp += 2 - - if ndigits == 0 { - *dst = Numeric{Int: big.NewInt(0), Status: Present} - return nil - } - - weight := int16(binary.BigEndian.Uint16(src[rp:])) - rp += 2 - sign := int16(binary.BigEndian.Uint16(src[rp:])) - rp += 2 - dscale := int16(binary.BigEndian.Uint16(src[rp:])) - rp += 2 - - if len(src[rp:]) < int(ndigits)*2 { - return errors.Errorf("numeric incomplete %v", src) - } - - accum := &big.Int{} - - for i := 0; i < int(ndigits+3)/4; i++ { - int64accum, bytesRead, digitsRead := nbaseDigitsToInt64(src[rp:]) - rp += bytesRead - - if i > 0 { - var mul *big.Int - switch digitsRead { - case 1: - mul = bigNBase - case 2: - mul = bigNBaseX2 - case 3: - mul = bigNBaseX3 - case 4: - mul = bigNBaseX4 - default: - return errors.Errorf("invalid digitsRead: %d (this can't happen)", digitsRead) - } - accum.Mul(accum, mul) - } - - accum.Add(accum, big.NewInt(int64accum)) - } - - exp := (int32(weight) - int32(ndigits) + 1) * 4 - - if dscale > 0 { - fracNBaseDigits := ndigits - weight - 1 - fracDecimalDigits := fracNBaseDigits * 4 - - if dscale > fracDecimalDigits { - multCount := int(dscale - fracDecimalDigits) - for i := 0; i < multCount; i++ { - accum.Mul(accum, big10) - exp-- - } - } else if dscale < fracDecimalDigits { - divCount := int(fracDecimalDigits - dscale) - for i := 0; i < divCount; i++ { - accum.Div(accum, big10) - exp++ - } - } - } - - reduced := &big.Int{} - remainder := &big.Int{} - if exp >= 0 { - for { - reduced.DivMod(accum, big10, remainder) - if remainder.Cmp(big0) != 0 { - break - } - accum.Set(reduced) - exp++ - } - } - - if sign != 0 { - accum.Neg(accum) - } - - *dst = Numeric{Int: accum, Exp: exp, Status: Present} - - return nil - -} - -func nbaseDigitsToInt64(src []byte) (accum int64, bytesRead, digitsRead int) { - digits := len(src) / 2 - if digits > 4 { - digits = 4 - } - - rp := 0 - - for i := 0; i < digits; i++ { - if i > 0 { - accum *= nbase - } - accum += int64(binary.BigEndian.Uint16(src[rp:])) - rp += 2 - } - - return accum, rp, digits -} - -func (src *Numeric) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = append(buf, src.Int.String()...) - buf = append(buf, 'e') - buf = append(buf, strconv.FormatInt(int64(src.Exp), 10)...) - return buf, nil -} - -func (src *Numeric) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var sign int16 - if src.Int.Cmp(big0) < 0 { - sign = 16384 - } - - absInt := &big.Int{} - wholePart := &big.Int{} - fracPart := &big.Int{} - remainder := &big.Int{} - absInt.Abs(src.Int) - - // Normalize absInt and exp to where exp is always a multiple of 4. This makes - // converting to 16-bit base 10,000 digits easier. - var exp int32 - switch src.Exp % 4 { - case 1, -3: - exp = src.Exp - 1 - absInt.Mul(absInt, big10) - case 2, -2: - exp = src.Exp - 2 - absInt.Mul(absInt, big100) - case 3, -1: - exp = src.Exp - 3 - absInt.Mul(absInt, big1000) - default: - exp = src.Exp - } - - if exp < 0 { - divisor := &big.Int{} - divisor.Exp(big10, big.NewInt(int64(-exp)), nil) - wholePart.DivMod(absInt, divisor, fracPart) - fracPart.Add(fracPart, divisor) - } else { - wholePart = absInt - } - - var wholeDigits, fracDigits []int16 - - for wholePart.Cmp(big0) != 0 { - wholePart.DivMod(wholePart, bigNBase, remainder) - wholeDigits = append(wholeDigits, int16(remainder.Int64())) - } - - if fracPart.Cmp(big0) != 0 { - for fracPart.Cmp(big1) != 0 { - fracPart.DivMod(fracPart, bigNBase, remainder) - fracDigits = append(fracDigits, int16(remainder.Int64())) - } - } - - buf = pgio.AppendInt16(buf, int16(len(wholeDigits)+len(fracDigits))) - - var weight int16 - if len(wholeDigits) > 0 { - weight = int16(len(wholeDigits) - 1) - if exp > 0 { - weight += int16(exp / 4) - } - } else { - weight = int16(exp/4) - 1 + int16(len(fracDigits)) - } - buf = pgio.AppendInt16(buf, weight) - - buf = pgio.AppendInt16(buf, sign) - - var dscale int16 - if src.Exp < 0 { - dscale = int16(-src.Exp) - } - buf = pgio.AppendInt16(buf, dscale) - - for i := len(wholeDigits) - 1; i >= 0; i-- { - buf = pgio.AppendInt16(buf, wholeDigits[i]) - } - - for i := len(fracDigits) - 1; i >= 0; i-- { - buf = pgio.AppendInt16(buf, fracDigits[i]) - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Numeric) Scan(src interface{}) error { - if src == nil { - *dst = Numeric{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Numeric) Value() (driver.Value, error) { - switch src.Status { - case Present: - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - - return string(buf), nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} diff --git a/pgtype/numeric_array.go b/pgtype/numeric_array.go deleted file mode 100644 index 1e8c5cda..00000000 --- a/pgtype/numeric_array.go +++ /dev/null @@ -1,385 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type NumericArray struct { - Elements []Numeric - Dimensions []ArrayDimension - Status Status -} - -func (dst *NumericArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = NumericArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []float32: - if value == nil { - *dst = NumericArray{Status: Null} - } else if len(value) == 0 { - *dst = NumericArray{Status: Present} - } else { - elements := make([]Numeric, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = NumericArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - case []float64: - if value == nil { - *dst = NumericArray{Status: Null} - } else if len(value) == 0 { - *dst = NumericArray{Status: Present} - } else { - elements := make([]Numeric, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = NumericArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - case []int64: - if value == nil { - *dst = NumericArray{Status: Null} - } else if len(value) == 0 { - *dst = NumericArray{Status: Present} - } else { - elements := make([]Numeric, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = NumericArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - case []uint64: - if value == nil { - *dst = NumericArray{Status: Null} - } else if len(value) == 0 { - *dst = NumericArray{Status: Present} - } else { - elements := make([]Numeric, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = NumericArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to NumericArray", value) - } - - return nil -} - -func (dst *NumericArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *NumericArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]float32: - *v = make([]float32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]float64: - *v = make([]float64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]int64: - *v = make([]int64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint64: - *v = make([]uint64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *NumericArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = NumericArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Numeric - - if len(uta.Elements) > 0 { - elements = make([]Numeric, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Numeric - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = NumericArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *NumericArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = NumericArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = NumericArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Numeric, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = NumericArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *NumericArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *NumericArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("numeric"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "numeric") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *NumericArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *NumericArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/numeric_array_test.go b/pgtype/numeric_array_test.go deleted file mode 100644 index b17a6461..00000000 --- a/pgtype/numeric_array_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package pgtype_test - -import ( - "math/big" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestNumericArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "numeric[]", []interface{}{ - &pgtype.NumericArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.NumericArray{ - Elements: []pgtype.Numeric{ - {Int: big.NewInt(1), Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.NumericArray{Status: pgtype.Null}, - &pgtype.NumericArray{ - Elements: []pgtype.Numeric{ - {Int: big.NewInt(1), Status: pgtype.Present}, - {Int: big.NewInt(2), Status: pgtype.Present}, - {Int: big.NewInt(3), Status: pgtype.Present}, - {Int: big.NewInt(4), Status: pgtype.Present}, - {Status: pgtype.Null}, - {Int: big.NewInt(6), Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.NumericArray{ - Elements: []pgtype.Numeric{ - {Int: big.NewInt(1), Status: pgtype.Present}, - {Int: big.NewInt(2), Status: pgtype.Present}, - {Int: big.NewInt(3), Status: pgtype.Present}, - {Int: big.NewInt(4), Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestNumericArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.NumericArray - }{ - { - source: []float32{1}, - result: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: []float64{1}, - result: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]float32)(nil)), - result: pgtype.NumericArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.NumericArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestNumericArrayAssignTo(t *testing.T) { - var float32Slice []float32 - var float64Slice []float64 - - simpleTests := []struct { - src pgtype.NumericArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &float32Slice, - expected: []float32{1}, - }, - { - src: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &float64Slice, - expected: []float64{1}, - }, - { - src: pgtype.NumericArray{Status: pgtype.Null}, - dst: &float32Slice, - expected: (([]float32)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.NumericArray - dst interface{} - }{ - { - src: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &float32Slice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } - -} diff --git a/pgtype/numeric_test.go b/pgtype/numeric_test.go deleted file mode 100644 index b723cc56..00000000 --- a/pgtype/numeric_test.go +++ /dev/null @@ -1,355 +0,0 @@ -package pgtype_test - -import ( - "math/big" - "math/rand" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -// For test purposes only. Note that it does not normalize values. e.g. (Int: 1, Exp: 3) will not equal (Int: 1000, Exp: 0) -func numericEqual(left, right *pgtype.Numeric) bool { - return left.Status == right.Status && - left.Exp == right.Exp && - ((left.Int == nil && right.Int == nil) || (left.Int != nil && right.Int != nil && left.Int.Cmp(right.Int) == 0)) -} - -// For test purposes only. -func numericNormalizedEqual(left, right *pgtype.Numeric) bool { - if left.Status != right.Status { - return false - } - - normLeft := &pgtype.Numeric{Int: (&big.Int{}).Set(left.Int), Status: left.Status} - normRight := &pgtype.Numeric{Int: (&big.Int{}).Set(right.Int), Status: right.Status} - - if left.Exp < right.Exp { - mul := (&big.Int{}).Exp(big.NewInt(10), big.NewInt(int64(right.Exp-left.Exp)), nil) - normRight.Int.Mul(normRight.Int, mul) - } else if left.Exp > right.Exp { - mul := (&big.Int{}).Exp(big.NewInt(10), big.NewInt(int64(left.Exp-right.Exp)), nil) - normLeft.Int.Mul(normLeft.Int, mul) - } - - return normLeft.Int.Cmp(normRight.Int) == 0 -} - -func mustParseBigInt(t *testing.T, src string) *big.Int { - i := &big.Int{} - if _, ok := i.SetString(src, 10); !ok { - t.Fatalf("could not parse big.Int: %s", src) - } - return i -} - -func TestNumericNormalize(t *testing.T) { - testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{ - { - SQL: "select '0'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(0), Exp: 0, Status: pgtype.Present}, - }, - { - SQL: "select '1'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(1), Exp: 0, Status: pgtype.Present}, - }, - { - SQL: "select '10.00'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(1000), Exp: -2, Status: pgtype.Present}, - }, - { - SQL: "select '1e-3'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(1), Exp: -3, Status: pgtype.Present}, - }, - { - SQL: "select '-1'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(-1), Exp: 0, Status: pgtype.Present}, - }, - { - SQL: "select '10000'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(1), Exp: 4, Status: pgtype.Present}, - }, - { - SQL: "select '3.14'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(314), Exp: -2, Status: pgtype.Present}, - }, - { - SQL: "select '1.1'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(11), Exp: -1, Status: pgtype.Present}, - }, - { - SQL: "select '100010001'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(100010001), Exp: 0, Status: pgtype.Present}, - }, - { - SQL: "select '100010001.0001'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(1000100010001), Exp: -4, Status: pgtype.Present}, - }, - { - SQL: "select '4237234789234789289347892374324872138321894178943189043890124832108934.43219085471578891547854892438945012347981'::numeric", - Value: &pgtype.Numeric{ - Int: mustParseBigInt(t, "423723478923478928934789237432487213832189417894318904389012483210893443219085471578891547854892438945012347981"), - Exp: -41, - Status: pgtype.Present, - }, - }, - { - SQL: "select '0.8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234'::numeric", - Value: &pgtype.Numeric{ - Int: mustParseBigInt(t, "8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234"), - Exp: -196, - Status: pgtype.Present, - }, - }, - { - SQL: "select '0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123'::numeric", - Value: &pgtype.Numeric{ - Int: mustParseBigInt(t, "123"), - Exp: -186, - Status: pgtype.Present, - }, - }, - }) -} - -func TestNumericTranscode(t *testing.T) { - testutil.TestSuccessfulTranscodeEqFunc(t, "numeric", []interface{}{ - &pgtype.Numeric{Int: big.NewInt(0), Exp: 0, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(1), Exp: 0, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(-1), Exp: 0, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(1), Exp: 6, Status: pgtype.Present}, - - // preserves significant zeroes - &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -1, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -2, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -3, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -4, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -5, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -6, Status: pgtype.Present}, - - &pgtype.Numeric{Int: big.NewInt(314), Exp: -2, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(123), Exp: -7, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(123), Exp: -8, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(123), Exp: -9, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(123), Exp: -1500, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "2437"), Exp: 23790, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "243723409723490243842378942378901237502734019231380123"), Exp: 23790, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 80, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "3723409723490243842378942378901237502734019231380123"), Exp: 81, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "723409723490243842378942378901237502734019231380123"), Exp: 82, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "23409723490243842378942378901237502734019231380123"), Exp: 83, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "3409723490243842378942378901237502734019231380123"), Exp: 84, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "913423409823409243892349028349023482934092340892390101"), Exp: -14021, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "13423409823409243892349028349023482934092340892390101"), Exp: -90, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "3423409823409243892349028349023482934092340892390101"), Exp: -91, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "423409823409243892349028349023482934092340892390101"), Exp: -92, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "23409823409243892349028349023482934092340892390101"), Exp: -93, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "3409823409243892349028349023482934092340892390101"), Exp: -94, Status: pgtype.Present}, - &pgtype.Numeric{Status: pgtype.Null}, - }, func(aa, bb interface{}) bool { - a := aa.(pgtype.Numeric) - b := bb.(pgtype.Numeric) - - return numericEqual(&a, &b) - }) - -} - -func TestNumericTranscodeFuzz(t *testing.T) { - r := rand.New(rand.NewSource(0)) - max := &big.Int{} - max.SetString("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 10) - - values := make([]interface{}, 0, 2000) - for i := 0; i < 10; i++ { - for j := -50; j < 50; j++ { - num := (&big.Int{}).Rand(r, max) - negNum := &big.Int{} - negNum.Neg(num) - values = append(values, &pgtype.Numeric{Int: num, Exp: int32(j), Status: pgtype.Present}) - values = append(values, &pgtype.Numeric{Int: negNum, Exp: int32(j), Status: pgtype.Present}) - } - } - - testutil.TestSuccessfulTranscodeEqFunc(t, "numeric", values, - func(aa, bb interface{}) bool { - a := aa.(pgtype.Numeric) - b := bb.(pgtype.Numeric) - - return numericNormalizedEqual(&a, &b) - }) -} - -func TestNumericSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result *pgtype.Numeric - }{ - {source: float32(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: float64(1), result: &pgtype.Numeric{Int: big.NewInt(1), 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}}, - {source: int64(1), result: &pgtype.Numeric{Int: big.NewInt(1), 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}}, - {source: int64(-1), result: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}}, - {source: uint8(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: uint16(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: uint32(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: uint64(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: "1", result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: _int8(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: float64(1000), result: &pgtype.Numeric{Int: big.NewInt(1), Exp: 3, Status: pgtype.Present}}, - {source: float64(1234), result: &pgtype.Numeric{Int: big.NewInt(1234), Exp: 0, Status: pgtype.Present}}, - {source: float64(12345678900), result: &pgtype.Numeric{Int: big.NewInt(123456789), Exp: 2, Status: pgtype.Present}}, - {source: float64(12345.678901), result: &pgtype.Numeric{Int: big.NewInt(12345678901), Exp: -6, Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - r := &pgtype.Numeric{} - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !numericEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestNumericAssignTo(t *testing.T) { - var i8 int8 - var i16 int16 - var i32 int32 - var i64 int64 - var i int - var ui8 uint8 - var ui16 uint16 - var ui32 uint32 - var ui64 uint64 - var ui uint - var pi8 *int8 - var _i8 _int8 - var _pi8 *_int8 - var f32 float32 - var f64 float64 - var pf32 *float32 - var pf64 *float64 - - simpleTests := []struct { - src *pgtype.Numeric - dst interface{} - expected interface{} - }{ - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &f32, expected: float32(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &f64, expected: float64(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Exp: -1, Status: pgtype.Present}, dst: &f32, expected: float32(4.2)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Exp: -1, Status: pgtype.Present}, dst: &f64, expected: float64(4.2)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Exp: 3, Status: pgtype.Present}, dst: &i64, expected: int64(42000)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src *pgtype.Numeric - dst interface{} - expected interface{} - }{ - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &pf32, expected: float32(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &pf64, expected: float64(42)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src *pgtype.Numeric - dst interface{} - }{ - {src: &pgtype.Numeric{Int: big.NewInt(150), Status: pgtype.Present}, dst: &i8}, - {src: &pgtype.Numeric{Int: big.NewInt(40000), Status: pgtype.Present}, dst: &i16}, - {src: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}, dst: &ui8}, - {src: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}, dst: &ui16}, - {src: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}, dst: &ui32}, - {src: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}, dst: &ui64}, - {src: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}, dst: &ui}, - {src: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Null}, dst: &i32}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} - -func TestNumericEncodeDecodeBinary(t *testing.T) { - ci := pgtype.NewConnInfo() - tests := []interface{}{ - 123, - 0.000012345, - 1.00002345, - } - - for i, tt := range tests { - toString := func(n *pgtype.Numeric) string { - ci := pgtype.NewConnInfo() - text, err := n.EncodeText(ci, nil) - if err != nil { - t.Errorf("%d: %v", i, err) - } - return string(text) - } - numeric := &pgtype.Numeric{} - numeric.Set(tt) - - encoded, err := numeric.EncodeBinary(ci, nil) - if err != nil { - t.Errorf("%d: %v", i, err) - } - decoded := &pgtype.Numeric{} - decoded.DecodeBinary(ci, encoded) - - text0 := toString(numeric) - text1 := toString(decoded) - - if text0 != text1 { - t.Errorf("%d: expected %v to equal to %v, but doesn't", i, text0, text1) - } - } -} diff --git a/pgtype/numrange.go b/pgtype/numrange.go deleted file mode 100644 index f3e25109..00000000 --- a/pgtype/numrange.go +++ /dev/null @@ -1,250 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Numrange struct { - Lower Numeric - Upper Numeric - LowerType BoundType - UpperType BoundType - Status Status -} - -func (dst *Numrange) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Numrange", src) -} - -func (dst *Numrange) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Numrange) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Numrange) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Numrange{Status: Null} - return nil - } - - utr, err := ParseUntypedTextRange(string(src)) - if err != nil { - return err - } - - *dst = Numrange{Status: Present} - - dst.LowerType = utr.LowerType - dst.UpperType = utr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeText(ci, []byte(utr.Lower)); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeText(ci, []byte(utr.Upper)); err != nil { - return err - } - } - - return nil -} - -func (dst *Numrange) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Numrange{Status: Null} - return nil - } - - ubr, err := ParseUntypedBinaryRange(src) - if err != nil { - return err - } - - *dst = Numrange{Status: Present} - - dst.LowerType = ubr.LowerType - dst.UpperType = ubr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeBinary(ci, ubr.Lower); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeBinary(ci, ubr.Upper); err != nil { - return err - } - } - - return nil -} - -func (src Numrange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - switch src.LowerType { - case Exclusive, Unbounded: - buf = append(buf, '(') - case Inclusive: - buf = append(buf, '[') - case Empty: - return append(buf, "empty"...), nil - default: - return nil, errors.Errorf("unknown lower bound type %v", src.LowerType) - } - - var err error - - if src.LowerType != Unbounded { - buf, err = src.Lower.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - } - - buf = append(buf, ',') - - if src.UpperType != Unbounded { - buf, err = src.Upper.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - } - - switch src.UpperType { - case Exclusive, Unbounded: - buf = append(buf, ')') - case Inclusive: - buf = append(buf, ']') - default: - return nil, errors.Errorf("unknown upper bound type %v", src.UpperType) - } - - return buf, nil -} - -func (src Numrange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var rangeType byte - switch src.LowerType { - case Inclusive: - rangeType |= lowerInclusiveMask - case Unbounded: - rangeType |= lowerUnboundedMask - case Exclusive: - case Empty: - return append(buf, emptyMask), nil - default: - return nil, errors.Errorf("unknown LowerType: %v", src.LowerType) - } - - switch src.UpperType { - case Inclusive: - rangeType |= upperInclusiveMask - case Unbounded: - rangeType |= upperUnboundedMask - case Exclusive: - default: - return nil, errors.Errorf("unknown UpperType: %v", src.UpperType) - } - - buf = append(buf, rangeType) - - var err error - - if src.LowerType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Lower.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - if src.UpperType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Upper.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Numrange) Scan(src interface{}) error { - if src == nil { - *dst = Numrange{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src Numrange) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/numrange_test.go b/pgtype/numrange_test.go deleted file mode 100644 index 610447fe..00000000 --- a/pgtype/numrange_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package pgtype_test - -import ( - "math/big" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestNumrangeTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "numrange", []interface{}{ - &pgtype.Numrange{ - LowerType: pgtype.Empty, - UpperType: pgtype.Empty, - Status: pgtype.Present, - }, - &pgtype.Numrange{ - Lower: pgtype.Numeric{Int: big.NewInt(-543), Exp: 3, Status: pgtype.Present}, - Upper: pgtype.Numeric{Int: big.NewInt(342), Exp: 1, Status: pgtype.Present}, - LowerType: pgtype.Inclusive, - UpperType: pgtype.Exclusive, - Status: pgtype.Present, - }, - &pgtype.Numrange{ - Lower: pgtype.Numeric{Int: big.NewInt(-42), Exp: 1, Status: pgtype.Present}, - Upper: pgtype.Numeric{Int: big.NewInt(-5), Exp: 0, Status: pgtype.Present}, - LowerType: pgtype.Inclusive, - UpperType: pgtype.Exclusive, - Status: pgtype.Present, - }, - &pgtype.Numrange{ - Lower: pgtype.Numeric{Int: big.NewInt(-42), Exp: 1, Status: pgtype.Present}, - LowerType: pgtype.Inclusive, - UpperType: pgtype.Unbounded, - Status: pgtype.Present, - }, - &pgtype.Numrange{ - Upper: pgtype.Numeric{Int: big.NewInt(-42), Exp: 1, Status: pgtype.Present}, - LowerType: pgtype.Unbounded, - UpperType: pgtype.Exclusive, - Status: pgtype.Present, - }, - &pgtype.Numrange{Status: pgtype.Null}, - }) -} diff --git a/pgtype/oid.go b/pgtype/oid.go deleted file mode 100644 index 593a5261..00000000 --- a/pgtype/oid.go +++ /dev/null @@ -1,81 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "strconv" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -// OID (Object Identifier Type) is, according to -// https://www.postgresql.org/docs/current/static/datatype-oid.html, used -// internally by PostgreSQL as a primary key for various system tables. It is -// currently implemented as an unsigned four-byte integer. Its definition can be -// found in src/include/postgres_ext.h in the PostgreSQL sources. Because it is -// so frequently required to be in a NOT NULL condition OID cannot be NULL. To -// allow for NULL OIDs use OIDValue. -type OID uint32 - -func (dst *OID) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - return errors.Errorf("cannot decode nil into OID") - } - - n, err := strconv.ParseUint(string(src), 10, 32) - if err != nil { - return err - } - - *dst = OID(n) - return nil -} - -func (dst *OID) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - return errors.Errorf("cannot decode nil into OID") - } - - if len(src) != 4 { - return errors.Errorf("invalid length: %v", len(src)) - } - - n := binary.BigEndian.Uint32(src) - *dst = OID(n) - return nil -} - -func (src OID) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - return append(buf, strconv.FormatUint(uint64(src), 10)...), nil -} - -func (src OID) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - return pgio.AppendUint32(buf, uint32(src)), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *OID) Scan(src interface{}) error { - if src == nil { - return errors.Errorf("cannot scan NULL into %T", src) - } - - switch src := src.(type) { - case int64: - *dst = OID(src) - return nil - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src OID) Value() (driver.Value, error) { - return int64(src), nil -} diff --git a/pgtype/oid_value.go b/pgtype/oid_value.go deleted file mode 100644 index 7eae4bf1..00000000 --- a/pgtype/oid_value.go +++ /dev/null @@ -1,55 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" -) - -// OIDValue (Object Identifier Type) is, according to -// https://www.postgresql.org/docs/current/static/datatype-OIDValue.html, used -// internally by PostgreSQL as a primary key for various system tables. It is -// currently implemented as an unsigned four-byte integer. Its definition can be -// found in src/include/postgres_ext.h in the PostgreSQL sources. -type OIDValue pguint32 - -// Set converts from src to dst. Note that as OIDValue is not a general -// number type Set does not do automatic type conversion as other number -// types do. -func (dst *OIDValue) Set(src interface{}) error { - return (*pguint32)(dst).Set(src) -} - -func (dst *OIDValue) Get() interface{} { - return (*pguint32)(dst).Get() -} - -// AssignTo assigns from src to dst. Note that as OIDValue is not a general number -// type AssignTo does not do automatic type conversion as other number types do. -func (src *OIDValue) AssignTo(dst interface{}) error { - return (*pguint32)(src).AssignTo(dst) -} - -func (dst *OIDValue) DecodeText(ci *ConnInfo, src []byte) error { - return (*pguint32)(dst).DecodeText(ci, src) -} - -func (dst *OIDValue) DecodeBinary(ci *ConnInfo, src []byte) error { - return (*pguint32)(dst).DecodeBinary(ci, src) -} - -func (src *OIDValue) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*pguint32)(src).EncodeText(ci, buf) -} - -func (src *OIDValue) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*pguint32)(src).EncodeBinary(ci, buf) -} - -// Scan implements the database/sql Scanner interface. -func (dst *OIDValue) Scan(src interface{}) error { - return (*pguint32)(dst).Scan(src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *OIDValue) Value() (driver.Value, error) { - return (*pguint32)(src).Value() -} diff --git a/pgtype/oid_value_test.go b/pgtype/oid_value_test.go deleted file mode 100644 index 462a5a28..00000000 --- a/pgtype/oid_value_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestOIDValueTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "oid", []interface{}{ - &pgtype.OIDValue{Uint: 42, Status: pgtype.Present}, - &pgtype.OIDValue{Status: pgtype.Null}, - }) -} - -func TestOIDValueSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.OIDValue - }{ - {source: uint32(1), result: pgtype.OIDValue{Uint: 1, Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var r pgtype.OIDValue - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestOIDValueAssignTo(t *testing.T) { - var ui32 uint32 - var pui32 *uint32 - - simpleTests := []struct { - src pgtype.OIDValue - dst interface{} - expected interface{} - }{ - {src: pgtype.OIDValue{Uint: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.OIDValue{Status: pgtype.Null}, dst: &pui32, expected: ((*uint32)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.OIDValue - dst interface{} - expected interface{} - }{ - {src: pgtype.OIDValue{Uint: 42, Status: pgtype.Present}, dst: &pui32, expected: uint32(42)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.OIDValue - dst interface{} - }{ - {src: pgtype.OIDValue{Status: pgtype.Null}, dst: &ui32}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/path.go b/pgtype/path.go deleted file mode 100644 index a4c6af77..00000000 --- a/pgtype/path.go +++ /dev/null @@ -1,196 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "fmt" - "math" - "strconv" - "strings" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Path struct { - P []Vec2 - Closed bool - Status Status -} - -func (dst *Path) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Path", src) -} - -func (dst *Path) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Path) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Path) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Path{Status: Null} - return nil - } - - if len(src) < 7 { - return errors.Errorf("invalid length for Path: %v", len(src)) - } - - closed := src[0] == '(' - points := make([]Vec2, 0) - - str := string(src[2:]) - - for { - end := strings.IndexByte(str, ',') - x, err := strconv.ParseFloat(str[:end], 64) - if err != nil { - return err - } - - str = str[end+1:] - end = strings.IndexByte(str, ')') - - y, err := strconv.ParseFloat(str[:end], 64) - if err != nil { - return err - } - - points = append(points, Vec2{x, y}) - - if end+3 < len(str) { - str = str[end+3:] - } else { - break - } - } - - *dst = Path{P: points, Closed: closed, Status: Present} - return nil -} - -func (dst *Path) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Path{Status: Null} - return nil - } - - if len(src) < 5 { - return errors.Errorf("invalid length for Path: %v", len(src)) - } - - closed := src[0] == 1 - pointCount := int(binary.BigEndian.Uint32(src[1:])) - - rp := 5 - - if 5+pointCount*16 != len(src) { - return errors.Errorf("invalid length for Path with %d points: %v", pointCount, len(src)) - } - - points := make([]Vec2, pointCount) - for i := 0; i < len(points); i++ { - x := binary.BigEndian.Uint64(src[rp:]) - rp += 8 - y := binary.BigEndian.Uint64(src[rp:]) - rp += 8 - points[i] = Vec2{math.Float64frombits(x), math.Float64frombits(y)} - } - - *dst = Path{ - P: points, - Closed: closed, - Status: Present, - } - return nil -} - -func (src *Path) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var startByte, endByte byte - if src.Closed { - startByte = '(' - endByte = ')' - } else { - startByte = '[' - endByte = ']' - } - buf = append(buf, startByte) - - for i, p := range src.P { - if i > 0 { - buf = append(buf, ',') - } - buf = append(buf, fmt.Sprintf(`(%s,%s)`, - strconv.FormatFloat(p.X, 'f', -1, 64), - strconv.FormatFloat(p.Y, 'f', -1, 64), - )...) - } - - return append(buf, endByte), nil -} - -func (src *Path) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var closeByte byte - if src.Closed { - closeByte = 1 - } - buf = append(buf, closeByte) - - buf = pgio.AppendInt32(buf, int32(len(src.P))) - - for _, p := range src.P { - buf = pgio.AppendUint64(buf, math.Float64bits(p.X)) - buf = pgio.AppendUint64(buf, math.Float64bits(p.Y)) - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Path) Scan(src interface{}) error { - if src == nil { - *dst = Path{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Path) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/path_test.go b/pgtype/path_test.go deleted file mode 100644 index 16e781f5..00000000 --- a/pgtype/path_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package pgtype_test - -import ( - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestPathTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "path", []interface{}{ - &pgtype.Path{ - P: []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}}, - Closed: false, - Status: pgtype.Present, - }, - &pgtype.Path{ - P: []pgtype.Vec2{{3.14, 1.678}, {7.1, 5.234}, {23.1, 9.34}}, - Closed: true, - Status: pgtype.Present, - }, - &pgtype.Path{ - P: []pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}}, - Closed: true, - Status: pgtype.Present, - }, - &pgtype.Path{Status: pgtype.Null}, - }) -} diff --git a/pgtype/pgtype.go b/pgtype/pgtype.go deleted file mode 100644 index cea4e1cd..00000000 --- a/pgtype/pgtype.go +++ /dev/null @@ -1,417 +0,0 @@ -package pgtype - -import ( - "database/sql" - "reflect" - - errors "golang.org/x/xerrors" -) - -// PostgreSQL oids for common types -const ( - BoolOID = 16 - ByteaOID = 17 - QCharOID = 18 - NameOID = 19 - Int8OID = 20 - Int2OID = 21 - Int4OID = 23 - TextOID = 25 - OIDOID = 26 - TIDOID = 27 - XIDOID = 28 - CIDOID = 29 - JSONOID = 114 - PointOID = 600 - LsegOID = 601 - PathOID = 602 - BoxOID = 603 - PolygonOID = 604 - LineOID = 628 - CIDROID = 650 - CIDRArrayOID = 651 - Float4OID = 700 - Float8OID = 701 - CircleOID = 718 - UnknownOID = 705 - MacaddrOID = 829 - InetOID = 869 - BoolArrayOID = 1000 - Int2ArrayOID = 1005 - Int4ArrayOID = 1007 - TextArrayOID = 1009 - ByteaArrayOID = 1001 - BPCharArrayOID = 1014 - VarcharArrayOID = 1015 - Int8ArrayOID = 1016 - Float4ArrayOID = 1021 - Float8ArrayOID = 1022 - ACLItemOID = 1033 - ACLItemArrayOID = 1034 - InetArrayOID = 1041 - BPCharOID = 1042 - VarcharOID = 1043 - DateOID = 1082 - TimestampOID = 1114 - TimestampArrayOID = 1115 - DateArrayOID = 1182 - TimestamptzOID = 1184 - TimestamptzArrayOID = 1185 - IntervalOID = 1186 - NumericArrayOID = 1231 - BitOID = 1560 - VarbitOID = 1562 - NumericOID = 1700 - RecordOID = 2249 - UUIDOID = 2950 - UUIDArrayOID = 2951 - JSONBOID = 3802 - DaterangeOID = 3812 - Int4rangeOID = 3904 - NumrangeOID = 3906 - TsrangeOID = 3908 - TstzrangeOID = 3910 - Int8rangeOID = 3926 -) - -type Status byte - -const ( - Undefined Status = iota - Null - Present -) - -type InfinityModifier int8 - -const ( - Infinity InfinityModifier = 1 - None InfinityModifier = 0 - NegativeInfinity InfinityModifier = -Infinity -) - -func (im InfinityModifier) String() string { - switch im { - case None: - return "none" - case Infinity: - return "infinity" - case NegativeInfinity: - return "-infinity" - default: - return "invalid" - } -} - -// PostgreSQL format codes -const ( - TextFormatCode = 0 - BinaryFormatCode = 1 -) - -type Value interface { - // Set converts and assigns src to itself. - Set(src interface{}) error - - // Get returns the simplest representation of Value. If the Value is Null or - // Undefined that is the return value. If no simpler representation is - // possible, then Get() returns Value. - Get() interface{} - - // AssignTo converts and assigns the Value to dst. It MUST make a deep copy of - // any reference types. - AssignTo(dst interface{}) error -} - -type BinaryDecoder interface { - // DecodeBinary decodes src into BinaryDecoder. If src is nil then the - // original SQL value is NULL. BinaryDecoder takes ownership of src. The - // caller MUST not use it again. - DecodeBinary(ci *ConnInfo, src []byte) error -} - -type TextDecoder interface { - // DecodeText decodes src into TextDecoder. If src is nil then the original - // SQL value is NULL. TextDecoder takes ownership of src. The caller MUST not - // use it again. - DecodeText(ci *ConnInfo, src []byte) error -} - -// BinaryEncoder is implemented by types that can encode themselves into the -// PostgreSQL binary wire format. -type BinaryEncoder interface { - // EncodeBinary should append the binary format of self to buf. If self is the - // SQL value NULL then append nothing and return (nil, nil). The caller of - // EncodeBinary is responsible for writing the correct NULL value or the - // length of the data written. - EncodeBinary(ci *ConnInfo, buf []byte) (newBuf []byte, err error) -} - -// TextEncoder is implemented by types that can encode themselves into the -// PostgreSQL text wire format. -type TextEncoder interface { - // EncodeText should append the text format of self to buf. If self is the - // SQL value NULL then append nothing and return (nil, nil). The caller of - // EncodeText is responsible for writing the correct NULL value or the - // length of the data written. - EncodeText(ci *ConnInfo, buf []byte) (newBuf []byte, err error) -} - -var errUndefined = errors.New("cannot encode status undefined") -var errBadStatus = errors.New("invalid status") - -type DataType struct { - Value Value - Name string - OID OID -} - -type ConnInfo struct { - oidToDataType map[OID]*DataType - nameToDataType map[string]*DataType - reflectTypeToDataType map[reflect.Type]*DataType -} - -func NewConnInfo() *ConnInfo { - ci := &ConnInfo{ - oidToDataType: make(map[OID]*DataType, 128), - nameToDataType: make(map[string]*DataType, 128), - reflectTypeToDataType: make(map[reflect.Type]*DataType, 128), - } - - ci.RegisterDataType(DataType{Value: &ACLItemArray{}, Name: "_aclitem", OID: ACLItemArrayOID}) - ci.RegisterDataType(DataType{Value: &BoolArray{}, Name: "_bool", OID: BoolArrayOID}) - ci.RegisterDataType(DataType{Value: &BPCharArray{}, Name: "_bpchar", OID: BPCharArrayOID}) - ci.RegisterDataType(DataType{Value: &ByteaArray{}, Name: "_bytea", OID: ByteaArrayOID}) - ci.RegisterDataType(DataType{Value: &CIDRArray{}, Name: "_cidr", OID: CIDRArrayOID}) - ci.RegisterDataType(DataType{Value: &DateArray{}, Name: "_date", OID: DateArrayOID}) - ci.RegisterDataType(DataType{Value: &Float4Array{}, Name: "_float4", OID: Float4ArrayOID}) - ci.RegisterDataType(DataType{Value: &Float8Array{}, Name: "_float8", OID: Float8ArrayOID}) - ci.RegisterDataType(DataType{Value: &InetArray{}, Name: "_inet", OID: InetArrayOID}) - ci.RegisterDataType(DataType{Value: &Int2Array{}, Name: "_int2", OID: Int2ArrayOID}) - ci.RegisterDataType(DataType{Value: &Int4Array{}, Name: "_int4", OID: Int4ArrayOID}) - ci.RegisterDataType(DataType{Value: &Int8Array{}, Name: "_int8", OID: Int8ArrayOID}) - ci.RegisterDataType(DataType{Value: &NumericArray{}, Name: "_numeric", OID: NumericArrayOID}) - ci.RegisterDataType(DataType{Value: &TextArray{}, Name: "_text", OID: TextArrayOID}) - ci.RegisterDataType(DataType{Value: &TimestampArray{}, Name: "_timestamp", OID: TimestampArrayOID}) - ci.RegisterDataType(DataType{Value: &TimestamptzArray{}, Name: "_timestamptz", OID: TimestamptzArrayOID}) - ci.RegisterDataType(DataType{Value: &UUIDArray{}, Name: "_uuid", OID: UUIDArrayOID}) - ci.RegisterDataType(DataType{Value: &VarcharArray{}, Name: "_varchar", OID: VarcharArrayOID}) - ci.RegisterDataType(DataType{Value: &ACLItem{}, Name: "aclitem", OID: ACLItemOID}) - ci.RegisterDataType(DataType{Value: &Bit{}, Name: "bit", OID: BitOID}) - ci.RegisterDataType(DataType{Value: &Bool{}, Name: "bool", OID: BoolOID}) - ci.RegisterDataType(DataType{Value: &Box{}, Name: "box", OID: BoxOID}) - ci.RegisterDataType(DataType{Value: &BPChar{}, Name: "bpchar", OID: BPCharOID}) - ci.RegisterDataType(DataType{Value: &Bytea{}, Name: "bytea", OID: ByteaOID}) - ci.RegisterDataType(DataType{Value: &QChar{}, Name: "char", OID: QCharOID}) - ci.RegisterDataType(DataType{Value: &CID{}, Name: "cid", OID: CIDOID}) - ci.RegisterDataType(DataType{Value: &CIDR{}, Name: "cidr", OID: CIDROID}) - ci.RegisterDataType(DataType{Value: &Circle{}, Name: "circle", OID: CircleOID}) - ci.RegisterDataType(DataType{Value: &Date{}, Name: "date", OID: DateOID}) - ci.RegisterDataType(DataType{Value: &Daterange{}, Name: "daterange", OID: DaterangeOID}) - ci.RegisterDataType(DataType{Value: &Float4{}, Name: "float4", OID: Float4OID}) - ci.RegisterDataType(DataType{Value: &Float8{}, Name: "float8", OID: Float8OID}) - ci.RegisterDataType(DataType{Value: &Inet{}, Name: "inet", OID: InetOID}) - ci.RegisterDataType(DataType{Value: &Int2{}, Name: "int2", OID: Int2OID}) - ci.RegisterDataType(DataType{Value: &Int4{}, Name: "int4", OID: Int4OID}) - ci.RegisterDataType(DataType{Value: &Int4range{}, Name: "int4range", OID: Int4rangeOID}) - ci.RegisterDataType(DataType{Value: &Int8{}, Name: "int8", OID: Int8OID}) - ci.RegisterDataType(DataType{Value: &Int8range{}, Name: "int8range", OID: Int8rangeOID}) - ci.RegisterDataType(DataType{Value: &Interval{}, Name: "interval", OID: IntervalOID}) - ci.RegisterDataType(DataType{Value: &JSON{}, Name: "json", OID: JSONOID}) - ci.RegisterDataType(DataType{Value: &JSONB{}, Name: "jsonb", OID: JSONBOID}) - ci.RegisterDataType(DataType{Value: &Line{}, Name: "line", OID: LineOID}) - ci.RegisterDataType(DataType{Value: &Lseg{}, Name: "lseg", OID: LsegOID}) - ci.RegisterDataType(DataType{Value: &Macaddr{}, Name: "macaddr", OID: MacaddrOID}) - ci.RegisterDataType(DataType{Value: &Name{}, Name: "name", OID: NameOID}) - ci.RegisterDataType(DataType{Value: &Numeric{}, Name: "numeric", OID: NumericOID}) - ci.RegisterDataType(DataType{Value: &Numrange{}, Name: "numrange", OID: NumrangeOID}) - ci.RegisterDataType(DataType{Value: &OIDValue{}, Name: "oid", OID: OIDOID}) - ci.RegisterDataType(DataType{Value: &Path{}, Name: "path", OID: PathOID}) - ci.RegisterDataType(DataType{Value: &Point{}, Name: "point", OID: PointOID}) - ci.RegisterDataType(DataType{Value: &Polygon{}, Name: "polygon", OID: PolygonOID}) - ci.RegisterDataType(DataType{Value: &Record{}, Name: "record", OID: RecordOID}) - ci.RegisterDataType(DataType{Value: &Text{}, Name: "text", OID: TextOID}) - ci.RegisterDataType(DataType{Value: &TID{}, Name: "tid", OID: TIDOID}) - ci.RegisterDataType(DataType{Value: &Timestamp{}, Name: "timestamp", OID: TimestampOID}) - ci.RegisterDataType(DataType{Value: &Timestamptz{}, Name: "timestamptz", OID: TimestamptzOID}) - ci.RegisterDataType(DataType{Value: &Tsrange{}, Name: "tsrange", OID: TsrangeOID}) - ci.RegisterDataType(DataType{Value: &Tstzrange{}, Name: "tstzrange", OID: TstzrangeOID}) - ci.RegisterDataType(DataType{Value: &Unknown{}, Name: "unknown", OID: UnknownOID}) - ci.RegisterDataType(DataType{Value: &UUID{}, Name: "uuid", OID: UUIDOID}) - ci.RegisterDataType(DataType{Value: &Varbit{}, Name: "varbit", OID: VarbitOID}) - ci.RegisterDataType(DataType{Value: &Varchar{}, Name: "varchar", OID: VarcharOID}) - ci.RegisterDataType(DataType{Value: &XID{}, Name: "xid", OID: XIDOID}) - - return ci -} - -func (ci *ConnInfo) InitializeDataTypes(nameOIDs map[string]OID) { - for name, oid := range nameOIDs { - var value Value - if t, ok := nameValues[name]; ok { - value = reflect.New(reflect.ValueOf(t).Elem().Type()).Interface().(Value) - } else { - value = &GenericText{} - } - ci.RegisterDataType(DataType{Value: value, Name: name, OID: oid}) - } -} - -func (ci *ConnInfo) RegisterDataType(t DataType) { - ci.oidToDataType[t.OID] = &t - ci.nameToDataType[t.Name] = &t - ci.reflectTypeToDataType[reflect.ValueOf(t.Value).Type()] = &t -} - -func (ci *ConnInfo) DataTypeForOID(oid OID) (*DataType, bool) { - dt, ok := ci.oidToDataType[oid] - return dt, ok -} - -func (ci *ConnInfo) DataTypeForName(name string) (*DataType, bool) { - dt, ok := ci.nameToDataType[name] - return dt, ok -} - -func (ci *ConnInfo) DataTypeForValue(v Value) (*DataType, bool) { - dt, ok := ci.reflectTypeToDataType[reflect.ValueOf(v).Type()] - return dt, ok -} - -// DeepCopy makes a deep copy of the ConnInfo. -func (ci *ConnInfo) DeepCopy() *ConnInfo { - ci2 := &ConnInfo{ - oidToDataType: make(map[OID]*DataType, len(ci.oidToDataType)), - nameToDataType: make(map[string]*DataType, len(ci.nameToDataType)), - reflectTypeToDataType: make(map[reflect.Type]*DataType, len(ci.reflectTypeToDataType)), - } - - for _, dt := range ci.oidToDataType { - ci2.RegisterDataType(DataType{ - Value: reflect.New(reflect.ValueOf(dt.Value).Elem().Type()).Interface().(Value), - Name: dt.Name, - OID: dt.OID, - }) - } - - return ci2 -} - -func (ci *ConnInfo) Scan(oid OID, formatCode int16, buf []byte, dest interface{}) error { - if dest, ok := dest.(BinaryDecoder); ok && formatCode == BinaryFormatCode { - return dest.DecodeBinary(ci, buf) - } - - if dest, ok := dest.(TextDecoder); ok && formatCode == TextFormatCode { - return dest.DecodeText(ci, buf) - } - - if dt, ok := ci.DataTypeForOID(oid); ok { - value := dt.Value - switch formatCode { - case TextFormatCode: - if textDecoder, ok := value.(TextDecoder); ok { - err := textDecoder.DecodeText(ci, buf) - if err != nil { - return err - } - } else { - return errors.Errorf("%T is not a pgtype.TextDecoder", value) - } - case BinaryFormatCode: - if binaryDecoder, ok := value.(BinaryDecoder); ok { - err := binaryDecoder.DecodeBinary(ci, buf) - if err != nil { - return err - } - } else { - return errors.Errorf("%T is not a pgtype.BinaryDecoder", value) - } - default: - return errors.Errorf("unknown format code: %v", formatCode) - } - - if scanner, ok := dest.(sql.Scanner); ok { - sqlSrc, err := DatabaseSQLValue(ci, value) - if err != nil { - return err - } - return scanner.Scan(sqlSrc) - } else { - return value.AssignTo(dest) - } - } - return errors.Errorf("unknown oid: %v", oid) -} - -var nameValues map[string]Value - -func init() { - nameValues = map[string]Value{ - "_aclitem": &ACLItemArray{}, - "_bool": &BoolArray{}, - "_bpchar": &BPCharArray{}, - "_bytea": &ByteaArray{}, - "_cidr": &CIDRArray{}, - "_date": &DateArray{}, - "_float4": &Float4Array{}, - "_float8": &Float8Array{}, - "_inet": &InetArray{}, - "_int2": &Int2Array{}, - "_int4": &Int4Array{}, - "_int8": &Int8Array{}, - "_numeric": &NumericArray{}, - "_text": &TextArray{}, - "_timestamp": &TimestampArray{}, - "_timestamptz": &TimestamptzArray{}, - "_uuid": &UUIDArray{}, - "_varchar": &VarcharArray{}, - "aclitem": &ACLItem{}, - "bit": &Bit{}, - "bool": &Bool{}, - "box": &Box{}, - "bpchar": &BPChar{}, - "bytea": &Bytea{}, - "char": &QChar{}, - "cid": &CID{}, - "cidr": &CIDR{}, - "circle": &Circle{}, - "date": &Date{}, - "daterange": &Daterange{}, - "float4": &Float4{}, - "float8": &Float8{}, - "hstore": &Hstore{}, - "inet": &Inet{}, - "int2": &Int2{}, - "int4": &Int4{}, - "int4range": &Int4range{}, - "int8": &Int8{}, - "int8range": &Int8range{}, - "interval": &Interval{}, - "json": &JSON{}, - "jsonb": &JSONB{}, - "line": &Line{}, - "lseg": &Lseg{}, - "macaddr": &Macaddr{}, - "name": &Name{}, - "numeric": &Numeric{}, - "numrange": &Numrange{}, - "oid": &OIDValue{}, - "path": &Path{}, - "point": &Point{}, - "polygon": &Polygon{}, - "record": &Record{}, - "text": &Text{}, - "tid": &TID{}, - "timestamp": &Timestamp{}, - "timestamptz": &Timestamptz{}, - "tsrange": &Tsrange{}, - "tstzrange": &Tstzrange{}, - "unknown": &Unknown{}, - "uuid": &UUID{}, - "varbit": &Varbit{}, - "varchar": &Varchar{}, - "xid": &XID{}, - } -} diff --git a/pgtype/pgtype_test.go b/pgtype/pgtype_test.go deleted file mode 100644 index 400c0591..00000000 --- a/pgtype/pgtype_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package pgtype_test - -import ( - "net" - "testing" - - _ "github.com/jackc/pgx/v4/stdlib" - _ "github.com/lib/pq" -) - -// Test for renamed types -type _string string -type _bool bool -type _int8 int8 -type _int16 int16 -type _int16Slice []int16 -type _int32Slice []int32 -type _int64Slice []int64 -type _float32Slice []float32 -type _float64Slice []float64 -type _byteSlice []byte - -func mustParseCIDR(t testing.TB, s string) *net.IPNet { - _, ipnet, err := net.ParseCIDR(s) - if err != nil { - t.Fatal(err) - } - - return ipnet -} - -func mustParseMacaddr(t testing.TB, s string) net.HardwareAddr { - addr, err := net.ParseMAC(s) - if err != nil { - t.Fatal(err) - } - - return addr -} diff --git a/pgtype/pguint32.go b/pgtype/pguint32.go deleted file mode 100644 index 21da9664..00000000 --- a/pgtype/pguint32.go +++ /dev/null @@ -1,162 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "math" - "strconv" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -// pguint32 is the core type that is used to implement PostgreSQL types such as -// CID and XID. -type pguint32 struct { - Uint uint32 - Status Status -} - -// Set converts from src to dst. Note that as pguint32 is not a general -// number type Set does not do automatic type conversion as other number -// types do. -func (dst *pguint32) Set(src interface{}) error { - switch value := src.(type) { - case int64: - if value < 0 { - return errors.Errorf("%d is less than minimum value for pguint32", value) - } - if value > math.MaxUint32 { - return errors.Errorf("%d is greater than maximum value for pguint32", value) - } - *dst = pguint32{Uint: uint32(value), Status: Present} - case uint32: - *dst = pguint32{Uint: value, Status: Present} - default: - return errors.Errorf("cannot convert %v to pguint32", value) - } - - return nil -} - -func (dst *pguint32) Get() interface{} { - switch dst.Status { - case Present: - return dst.Uint - case Null: - return nil - default: - return dst.Status - } -} - -// AssignTo assigns from src to dst. Note that as pguint32 is not a general number -// type AssignTo does not do automatic type conversion as other number types do. -func (src *pguint32) AssignTo(dst interface{}) error { - switch v := dst.(type) { - case *uint32: - if src.Status == Present { - *v = src.Uint - } else { - return errors.Errorf("cannot assign %v into %T", src, dst) - } - case **uint32: - if src.Status == Present { - n := src.Uint - *v = &n - } else { - *v = nil - } - } - - return nil -} - -func (dst *pguint32) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = pguint32{Status: Null} - return nil - } - - n, err := strconv.ParseUint(string(src), 10, 32) - if err != nil { - return err - } - - *dst = pguint32{Uint: uint32(n), Status: Present} - return nil -} - -func (dst *pguint32) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = pguint32{Status: Null} - return nil - } - - if len(src) != 4 { - return errors.Errorf("invalid length: %v", len(src)) - } - - n := binary.BigEndian.Uint32(src) - *dst = pguint32{Uint: n, Status: Present} - return nil -} - -func (src *pguint32) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, strconv.FormatUint(uint64(src.Uint), 10)...), nil -} - -func (src *pguint32) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return pgio.AppendUint32(buf, src.Uint), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *pguint32) Scan(src interface{}) error { - if src == nil { - *dst = pguint32{Status: Null} - return nil - } - - switch src := src.(type) { - case uint32: - *dst = pguint32{Uint: src, Status: Present} - return nil - case int64: - *dst = pguint32{Uint: uint32(src), Status: Present} - return nil - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *pguint32) Value() (driver.Value, error) { - switch src.Status { - case Present: - return int64(src.Uint), nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} diff --git a/pgtype/point.go b/pgtype/point.go deleted file mode 100644 index 89f2359b..00000000 --- a/pgtype/point.go +++ /dev/null @@ -1,142 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "fmt" - "math" - "strconv" - "strings" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Vec2 struct { - X float64 - Y float64 -} - -type Point struct { - P Vec2 - Status Status -} - -func (dst *Point) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Point", src) -} - -func (dst *Point) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Point) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Point) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Point{Status: Null} - return nil - } - - if len(src) < 5 { - return errors.Errorf("invalid length for point: %v", len(src)) - } - - parts := strings.SplitN(string(src[1:len(src)-1]), ",", 2) - if len(parts) < 2 { - return errors.Errorf("invalid format for point") - } - - x, err := strconv.ParseFloat(parts[0], 64) - if err != nil { - return err - } - - y, err := strconv.ParseFloat(parts[1], 64) - if err != nil { - return err - } - - *dst = Point{P: Vec2{x, y}, Status: Present} - return nil -} - -func (dst *Point) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Point{Status: Null} - return nil - } - - if len(src) != 16 { - return errors.Errorf("invalid length for point: %v", len(src)) - } - - x := binary.BigEndian.Uint64(src) - y := binary.BigEndian.Uint64(src[8:]) - - *dst = Point{ - P: Vec2{math.Float64frombits(x), math.Float64frombits(y)}, - Status: Present, - } - return nil -} - -func (src *Point) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, fmt.Sprintf(`(%s,%s)`, - strconv.FormatFloat(src.P.X, 'f', -1, 64), - strconv.FormatFloat(src.P.Y, 'f', -1, 64), - )...), nil -} - -func (src *Point) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = pgio.AppendUint64(buf, math.Float64bits(src.P.X)) - buf = pgio.AppendUint64(buf, math.Float64bits(src.P.Y)) - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Point) Scan(src interface{}) error { - if src == nil { - *dst = Point{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Point) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/point_test.go b/pgtype/point_test.go deleted file mode 100644 index 017bfc03..00000000 --- a/pgtype/point_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package pgtype_test - -import ( - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestPointTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "point", []interface{}{ - &pgtype.Point{P: pgtype.Vec2{1.234, 5.6789012345}, Status: pgtype.Present}, - &pgtype.Point{P: pgtype.Vec2{-1.234, -5.6789}, Status: pgtype.Present}, - &pgtype.Point{Status: pgtype.Null}, - }) -} diff --git a/pgtype/polygon.go b/pgtype/polygon.go deleted file mode 100644 index e739c71b..00000000 --- a/pgtype/polygon.go +++ /dev/null @@ -1,177 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "fmt" - "math" - "strconv" - "strings" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Polygon struct { - P []Vec2 - Status Status -} - -func (dst *Polygon) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Polygon", src) -} - -func (dst *Polygon) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Polygon) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Polygon) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Polygon{Status: Null} - return nil - } - - if len(src) < 7 { - return errors.Errorf("invalid length for Polygon: %v", len(src)) - } - - points := make([]Vec2, 0) - - str := string(src[2:]) - - for { - end := strings.IndexByte(str, ',') - x, err := strconv.ParseFloat(str[:end], 64) - if err != nil { - return err - } - - str = str[end+1:] - end = strings.IndexByte(str, ')') - - y, err := strconv.ParseFloat(str[:end], 64) - if err != nil { - return err - } - - points = append(points, Vec2{x, y}) - - if end+3 < len(str) { - str = str[end+3:] - } else { - break - } - } - - *dst = Polygon{P: points, Status: Present} - return nil -} - -func (dst *Polygon) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Polygon{Status: Null} - return nil - } - - if len(src) < 5 { - return errors.Errorf("invalid length for Polygon: %v", len(src)) - } - - pointCount := int(binary.BigEndian.Uint32(src)) - rp := 4 - - if 4+pointCount*16 != len(src) { - return errors.Errorf("invalid length for Polygon with %d points: %v", pointCount, len(src)) - } - - points := make([]Vec2, pointCount) - for i := 0; i < len(points); i++ { - x := binary.BigEndian.Uint64(src[rp:]) - rp += 8 - y := binary.BigEndian.Uint64(src[rp:]) - rp += 8 - points[i] = Vec2{math.Float64frombits(x), math.Float64frombits(y)} - } - - *dst = Polygon{ - P: points, - Status: Present, - } - return nil -} - -func (src *Polygon) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = append(buf, '(') - - for i, p := range src.P { - if i > 0 { - buf = append(buf, ',') - } - buf = append(buf, fmt.Sprintf(`(%s,%s)`, - strconv.FormatFloat(p.X, 'f', -1, 64), - strconv.FormatFloat(p.Y, 'f', -1, 64), - )...) - } - - return append(buf, ')'), nil -} - -func (src *Polygon) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = pgio.AppendInt32(buf, int32(len(src.P))) - - for _, p := range src.P { - buf = pgio.AppendUint64(buf, math.Float64bits(p.X)) - buf = pgio.AppendUint64(buf, math.Float64bits(p.Y)) - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Polygon) Scan(src interface{}) error { - if src == nil { - *dst = Polygon{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Polygon) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/polygon_test.go b/pgtype/polygon_test.go deleted file mode 100644 index 3bafebfc..00000000 --- a/pgtype/polygon_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package pgtype_test - -import ( - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestPolygonTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "polygon", []interface{}{ - &pgtype.Polygon{ - P: []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}, {5.0, 3.234}}, - Status: pgtype.Present, - }, - &pgtype.Polygon{ - P: []pgtype.Vec2{{3.14, -1.678}, {7.1, -5.234}, {23.1, 9.34}}, - Status: pgtype.Present, - }, - &pgtype.Polygon{Status: pgtype.Null}, - }) -} diff --git a/pgtype/qchar.go b/pgtype/qchar.go deleted file mode 100644 index 5e77dc38..00000000 --- a/pgtype/qchar.go +++ /dev/null @@ -1,146 +0,0 @@ -package pgtype - -import ( - "math" - "strconv" - - errors "golang.org/x/xerrors" -) - -// QChar is for PostgreSQL's special 8-bit-only "char" type more akin to the C -// language's char type, or Go's byte type. (Note that the name in PostgreSQL -// itself is "char", in double-quotes, and not char.) It gets used a lot in -// PostgreSQL's system tables to hold a single ASCII character value (eg -// pg_class.relkind). It is named Qchar for quoted char to disambiguate from SQL -// standard type char. -// -// Not all possible values of QChar are representable in the text format. -// Therefore, QChar does not implement TextEncoder and TextDecoder. In -// addition, database/sql Scanner and database/sql/driver Value are not -// implemented. -type QChar struct { - Int int8 - Status Status -} - -func (dst *QChar) Set(src interface{}) error { - if src == nil { - *dst = QChar{Status: Null} - return nil - } - - switch value := src.(type) { - case int8: - *dst = QChar{Int: value, Status: Present} - case uint8: - if value > math.MaxInt8 { - return errors.Errorf("%d is greater than maximum value for QChar", value) - } - *dst = QChar{Int: int8(value), Status: Present} - case int16: - if value < math.MinInt8 { - return errors.Errorf("%d is greater than maximum value for QChar", value) - } - if value > math.MaxInt8 { - return errors.Errorf("%d is greater than maximum value for QChar", value) - } - *dst = QChar{Int: int8(value), Status: Present} - case uint16: - if value > math.MaxInt8 { - return errors.Errorf("%d is greater than maximum value for QChar", value) - } - *dst = QChar{Int: int8(value), Status: Present} - case int32: - if value < math.MinInt8 { - return errors.Errorf("%d is greater than maximum value for QChar", value) - } - if value > math.MaxInt8 { - return errors.Errorf("%d is greater than maximum value for QChar", value) - } - *dst = QChar{Int: int8(value), Status: Present} - case uint32: - if value > math.MaxInt8 { - return errors.Errorf("%d is greater than maximum value for QChar", value) - } - *dst = QChar{Int: int8(value), Status: Present} - case int64: - if value < math.MinInt8 { - return errors.Errorf("%d is greater than maximum value for QChar", value) - } - if value > math.MaxInt8 { - return errors.Errorf("%d is greater than maximum value for QChar", value) - } - *dst = QChar{Int: int8(value), Status: Present} - case uint64: - if value > math.MaxInt8 { - return errors.Errorf("%d is greater than maximum value for QChar", value) - } - *dst = QChar{Int: int8(value), Status: Present} - case int: - if value < math.MinInt8 { - return errors.Errorf("%d is greater than maximum value for QChar", value) - } - if value > math.MaxInt8 { - return errors.Errorf("%d is greater than maximum value for QChar", value) - } - *dst = QChar{Int: int8(value), Status: Present} - case uint: - if value > math.MaxInt8 { - return errors.Errorf("%d is greater than maximum value for QChar", value) - } - *dst = QChar{Int: int8(value), Status: Present} - case string: - num, err := strconv.ParseInt(value, 10, 8) - if err != nil { - return err - } - *dst = QChar{Int: int8(num), Status: Present} - default: - if originalSrc, ok := underlyingNumberType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to QChar", value) - } - - return nil -} - -func (dst *QChar) Get() interface{} { - switch dst.Status { - case Present: - return dst.Int - case Null: - return nil - default: - return dst.Status - } -} - -func (src *QChar) AssignTo(dst interface{}) error { - return int64AssignTo(int64(src.Int), src.Status, dst) -} - -func (dst *QChar) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = QChar{Status: Null} - return nil - } - - if len(src) != 1 { - return errors.Errorf(`invalid length for "char": %v`, len(src)) - } - - *dst = QChar{Int: int8(src[0]), Status: Present} - return nil -} - -func (src *QChar) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, byte(src.Int)), nil -} diff --git a/pgtype/qchar_test.go b/pgtype/qchar_test.go deleted file mode 100644 index 3b50bb3e..00000000 --- a/pgtype/qchar_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package pgtype_test - -import ( - "math" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestQCharTranscode(t *testing.T) { - testutil.TestPgxSuccessfulTranscodeEqFunc(t, `"char"`, []interface{}{ - &pgtype.QChar{Int: math.MinInt8, Status: pgtype.Present}, - &pgtype.QChar{Int: -1, Status: pgtype.Present}, - &pgtype.QChar{Int: 0, Status: pgtype.Present}, - &pgtype.QChar{Int: 1, Status: pgtype.Present}, - &pgtype.QChar{Int: math.MaxInt8, Status: pgtype.Present}, - &pgtype.QChar{Int: 0, Status: pgtype.Null}, - }, func(a, b interface{}) bool { - return reflect.DeepEqual(a, b) - }) -} - -func TestQCharSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.QChar - }{ - {source: int8(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: int16(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: int32(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: int64(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: int8(-1), result: pgtype.QChar{Int: -1, Status: pgtype.Present}}, - {source: int16(-1), result: pgtype.QChar{Int: -1, Status: pgtype.Present}}, - {source: int32(-1), result: pgtype.QChar{Int: -1, Status: pgtype.Present}}, - {source: int64(-1), result: pgtype.QChar{Int: -1, Status: pgtype.Present}}, - {source: uint8(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: uint16(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: uint32(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: uint64(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: "1", result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: _int8(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var r pgtype.QChar - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestQCharAssignTo(t *testing.T) { - var i8 int8 - var i16 int16 - var i32 int32 - var i64 int64 - var i int - var ui8 uint8 - var ui16 uint16 - var ui32 uint32 - var ui64 uint64 - var ui uint - var pi8 *int8 - var _i8 _int8 - var _pi8 *_int8 - - simpleTests := []struct { - src pgtype.QChar - dst interface{} - expected interface{} - }{ - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &i8, expected: int8(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: pgtype.QChar{Int: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: pgtype.QChar{Int: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.QChar - dst interface{} - expected interface{} - }{ - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &pi8, expected: int8(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &_pi8, expected: _int8(42)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.QChar - dst interface{} - }{ - {src: pgtype.QChar{Int: -1, Status: pgtype.Present}, dst: &ui8}, - {src: pgtype.QChar{Int: -1, Status: pgtype.Present}, dst: &ui16}, - {src: pgtype.QChar{Int: -1, Status: pgtype.Present}, dst: &ui32}, - {src: pgtype.QChar{Int: -1, Status: pgtype.Present}, dst: &ui64}, - {src: pgtype.QChar{Int: -1, Status: pgtype.Present}, dst: &ui}, - {src: pgtype.QChar{Int: 0, Status: pgtype.Null}, dst: &i16}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/range.go b/pgtype/range.go deleted file mode 100644 index 35b80ced..00000000 --- a/pgtype/range.go +++ /dev/null @@ -1,278 +0,0 @@ -package pgtype - -import ( - "bytes" - "encoding/binary" - - errors "golang.org/x/xerrors" -) - -type BoundType byte - -const ( - Inclusive = BoundType('i') - Exclusive = BoundType('e') - Unbounded = BoundType('U') - Empty = BoundType('E') -) - -func (bt BoundType) String() string { - return string(bt) -} - -type UntypedTextRange struct { - Lower string - Upper string - LowerType BoundType - UpperType BoundType -} - -func ParseUntypedTextRange(src string) (*UntypedTextRange, error) { - utr := &UntypedTextRange{} - if src == "empty" { - utr.LowerType = Empty - utr.UpperType = Empty - return utr, nil - } - - buf := bytes.NewBufferString(src) - - skipWhitespace(buf) - - r, _, err := buf.ReadRune() - if err != nil { - return nil, errors.Errorf("invalid lower bound: %v", err) - } - switch r { - case '(': - utr.LowerType = Exclusive - case '[': - utr.LowerType = Inclusive - default: - return nil, errors.Errorf("missing lower bound, instead got: %v", string(r)) - } - - r, _, err = buf.ReadRune() - if err != nil { - return nil, errors.Errorf("invalid lower value: %v", err) - } - buf.UnreadRune() - - if r == ',' { - utr.LowerType = Unbounded - } else { - utr.Lower, err = rangeParseValue(buf) - if err != nil { - return nil, errors.Errorf("invalid lower value: %v", err) - } - } - - r, _, err = buf.ReadRune() - if err != nil { - return nil, errors.Errorf("missing range separator: %v", err) - } - if r != ',' { - return nil, errors.Errorf("missing range separator: %v", r) - } - - r, _, err = buf.ReadRune() - if err != nil { - return nil, errors.Errorf("invalid upper value: %v", err) - } - - if r == ')' || r == ']' { - utr.UpperType = Unbounded - } else { - buf.UnreadRune() - utr.Upper, err = rangeParseValue(buf) - if err != nil { - return nil, errors.Errorf("invalid upper value: %v", err) - } - - r, _, err = buf.ReadRune() - if err != nil { - return nil, errors.Errorf("missing upper bound: %v", err) - } - switch r { - case ')': - utr.UpperType = Exclusive - case ']': - utr.UpperType = Inclusive - default: - return nil, errors.Errorf("missing upper bound, instead got: %v", string(r)) - } - } - - skipWhitespace(buf) - - if buf.Len() > 0 { - return nil, errors.Errorf("unexpected trailing data: %v", buf.String()) - } - - return utr, nil -} - -func rangeParseValue(buf *bytes.Buffer) (string, error) { - r, _, err := buf.ReadRune() - if err != nil { - return "", err - } - if r == '"' { - return rangeParseQuotedValue(buf) - } - buf.UnreadRune() - - s := &bytes.Buffer{} - - for { - r, _, err := buf.ReadRune() - if err != nil { - return "", err - } - - switch r { - case '\\': - r, _, err = buf.ReadRune() - if err != nil { - return "", err - } - case ',', '[', ']', '(', ')': - buf.UnreadRune() - return s.String(), nil - } - - s.WriteRune(r) - } -} - -func rangeParseQuotedValue(buf *bytes.Buffer) (string, error) { - s := &bytes.Buffer{} - - for { - r, _, err := buf.ReadRune() - if err != nil { - return "", err - } - - switch r { - case '\\': - r, _, err = buf.ReadRune() - if err != nil { - return "", err - } - case '"': - r, _, err = buf.ReadRune() - if err != nil { - return "", err - } - if r != '"' { - buf.UnreadRune() - return s.String(), nil - } - } - s.WriteRune(r) - } -} - -type UntypedBinaryRange struct { - Lower []byte - Upper []byte - LowerType BoundType - UpperType BoundType -} - -// 0 = () = 00000 -// 1 = empty = 00001 -// 2 = [) = 00010 -// 4 = (] = 00100 -// 6 = [] = 00110 -// 8 = ) = 01000 -// 12 = ] = 01100 -// 16 = ( = 10000 -// 18 = [ = 10010 -// 24 = = 11000 - -const emptyMask = 1 -const lowerInclusiveMask = 2 -const upperInclusiveMask = 4 -const lowerUnboundedMask = 8 -const upperUnboundedMask = 16 - -func ParseUntypedBinaryRange(src []byte) (*UntypedBinaryRange, error) { - ubr := &UntypedBinaryRange{} - - if len(src) == 0 { - return nil, errors.Errorf("range too short: %v", len(src)) - } - - rangeType := src[0] - rp := 1 - - if rangeType&emptyMask > 0 { - if len(src[rp:]) > 0 { - return nil, errors.Errorf("unexpected trailing bytes parsing empty range: %v", len(src[rp:])) - } - ubr.LowerType = Empty - ubr.UpperType = Empty - return ubr, nil - } - - if rangeType&lowerInclusiveMask > 0 { - ubr.LowerType = Inclusive - } else if rangeType&lowerUnboundedMask > 0 { - ubr.LowerType = Unbounded - } else { - ubr.LowerType = Exclusive - } - - if rangeType&upperInclusiveMask > 0 { - ubr.UpperType = Inclusive - } else if rangeType&upperUnboundedMask > 0 { - ubr.UpperType = Unbounded - } else { - ubr.UpperType = Exclusive - } - - if ubr.LowerType == Unbounded && ubr.UpperType == Unbounded { - if len(src[rp:]) > 0 { - return nil, errors.Errorf("unexpected trailing bytes parsing unbounded range: %v", len(src[rp:])) - } - return ubr, nil - } - - if len(src[rp:]) < 4 { - return nil, errors.Errorf("too few bytes for size: %v", src[rp:]) - } - valueLen := int(binary.BigEndian.Uint32(src[rp:])) - rp += 4 - - val := src[rp : rp+valueLen] - rp += valueLen - - if ubr.LowerType != Unbounded { - ubr.Lower = val - } else { - ubr.Upper = val - if len(src[rp:]) > 0 { - return nil, errors.Errorf("unexpected trailing bytes parsing range: %v", len(src[rp:])) - } - return ubr, nil - } - - if ubr.UpperType != Unbounded { - if len(src[rp:]) < 4 { - return nil, errors.Errorf("too few bytes for size: %v", src[rp:]) - } - valueLen := int(binary.BigEndian.Uint32(src[rp:])) - rp += 4 - ubr.Upper = src[rp : rp+valueLen] - rp += valueLen - } - - if len(src[rp:]) > 0 { - return nil, errors.Errorf("unexpected trailing bytes parsing range: %v", len(src[rp:])) - } - - return ubr, nil - -} diff --git a/pgtype/range_test.go b/pgtype/range_test.go deleted file mode 100644 index 9e16df59..00000000 --- a/pgtype/range_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package pgtype - -import ( - "bytes" - "testing" -) - -func TestParseUntypedTextRange(t *testing.T) { - tests := []struct { - src string - result UntypedTextRange - err error - }{ - { - src: `[1,2)`, - result: UntypedTextRange{Lower: "1", Upper: "2", LowerType: Inclusive, UpperType: Exclusive}, - err: nil, - }, - { - src: `[1,2]`, - result: UntypedTextRange{Lower: "1", Upper: "2", LowerType: Inclusive, UpperType: Inclusive}, - err: nil, - }, - { - src: `(1,3)`, - result: UntypedTextRange{Lower: "1", Upper: "3", LowerType: Exclusive, UpperType: Exclusive}, - err: nil, - }, - { - src: ` [1,2) `, - result: UntypedTextRange{Lower: "1", Upper: "2", LowerType: Inclusive, UpperType: Exclusive}, - err: nil, - }, - { - src: `[ foo , bar )`, - result: UntypedTextRange{Lower: " foo ", Upper: " bar ", LowerType: Inclusive, UpperType: Exclusive}, - err: nil, - }, - { - src: `["foo","bar")`, - result: UntypedTextRange{Lower: "foo", Upper: "bar", LowerType: Inclusive, UpperType: Exclusive}, - err: nil, - }, - { - src: `["f""oo","b""ar")`, - result: UntypedTextRange{Lower: `f"oo`, Upper: `b"ar`, LowerType: Inclusive, UpperType: Exclusive}, - err: nil, - }, - { - src: `["f""oo","b""ar")`, - result: UntypedTextRange{Lower: `f"oo`, Upper: `b"ar`, LowerType: Inclusive, UpperType: Exclusive}, - err: nil, - }, - { - src: `["","bar")`, - result: UntypedTextRange{Lower: ``, Upper: `bar`, LowerType: Inclusive, UpperType: Exclusive}, - err: nil, - }, - { - src: `[f\"oo\,,b\\ar\))`, - result: UntypedTextRange{Lower: `f"oo,`, Upper: `b\ar)`, LowerType: Inclusive, UpperType: Exclusive}, - err: nil, - }, - { - src: `empty`, - result: UntypedTextRange{Lower: "", Upper: "", LowerType: Empty, UpperType: Empty}, - err: nil, - }, - } - - for i, tt := range tests { - r, err := ParseUntypedTextRange(tt.src) - if err != tt.err { - t.Errorf("%d. `%v`: expected err %v, got %v", i, tt.src, tt.err, err) - continue - } - - if r.LowerType != tt.result.LowerType { - t.Errorf("%d. `%v`: expected result lower type %v, got %v", i, tt.src, string(tt.result.LowerType), string(r.LowerType)) - } - - if r.UpperType != tt.result.UpperType { - t.Errorf("%d. `%v`: expected result upper type %v, got %v", i, tt.src, string(tt.result.UpperType), string(r.UpperType)) - } - - if r.Lower != tt.result.Lower { - t.Errorf("%d. `%v`: expected result lower %v, got %v", i, tt.src, tt.result.Lower, r.Lower) - } - - if r.Upper != tt.result.Upper { - t.Errorf("%d. `%v`: expected result upper %v, got %v", i, tt.src, tt.result.Upper, r.Upper) - } - } -} - -func TestParseUntypedBinaryRange(t *testing.T) { - tests := []struct { - src []byte - result UntypedBinaryRange - err error - }{ - { - src: []byte{0, 0, 0, 0, 2, 0, 4, 0, 0, 0, 2, 0, 5}, - result: UntypedBinaryRange{Lower: []byte{0, 4}, Upper: []byte{0, 5}, LowerType: Exclusive, UpperType: Exclusive}, - err: nil, - }, - { - src: []byte{1}, - result: UntypedBinaryRange{Lower: nil, Upper: nil, LowerType: Empty, UpperType: Empty}, - err: nil, - }, - { - src: []byte{2, 0, 0, 0, 2, 0, 4, 0, 0, 0, 2, 0, 5}, - result: UntypedBinaryRange{Lower: []byte{0, 4}, Upper: []byte{0, 5}, LowerType: Inclusive, UpperType: Exclusive}, - err: nil, - }, - { - src: []byte{4, 0, 0, 0, 2, 0, 4, 0, 0, 0, 2, 0, 5}, - result: UntypedBinaryRange{Lower: []byte{0, 4}, Upper: []byte{0, 5}, LowerType: Exclusive, UpperType: Inclusive}, - err: nil, - }, - { - src: []byte{6, 0, 0, 0, 2, 0, 4, 0, 0, 0, 2, 0, 5}, - result: UntypedBinaryRange{Lower: []byte{0, 4}, Upper: []byte{0, 5}, LowerType: Inclusive, UpperType: Inclusive}, - err: nil, - }, - { - src: []byte{8, 0, 0, 0, 2, 0, 5}, - result: UntypedBinaryRange{Lower: nil, Upper: []byte{0, 5}, LowerType: Unbounded, UpperType: Exclusive}, - err: nil, - }, - { - src: []byte{12, 0, 0, 0, 2, 0, 5}, - result: UntypedBinaryRange{Lower: nil, Upper: []byte{0, 5}, LowerType: Unbounded, UpperType: Inclusive}, - err: nil, - }, - { - src: []byte{16, 0, 0, 0, 2, 0, 4}, - result: UntypedBinaryRange{Lower: []byte{0, 4}, Upper: nil, LowerType: Exclusive, UpperType: Unbounded}, - err: nil, - }, - { - src: []byte{18, 0, 0, 0, 2, 0, 4}, - result: UntypedBinaryRange{Lower: []byte{0, 4}, Upper: nil, LowerType: Inclusive, UpperType: Unbounded}, - err: nil, - }, - { - src: []byte{24}, - result: UntypedBinaryRange{Lower: nil, Upper: nil, LowerType: Unbounded, UpperType: Unbounded}, - err: nil, - }, - } - - for i, tt := range tests { - r, err := ParseUntypedBinaryRange(tt.src) - if err != tt.err { - t.Errorf("%d. `%v`: expected err %v, got %v", i, tt.src, tt.err, err) - continue - } - - if r.LowerType != tt.result.LowerType { - t.Errorf("%d. `%v`: expected result lower type %v, got %v", i, tt.src, string(tt.result.LowerType), string(r.LowerType)) - } - - if r.UpperType != tt.result.UpperType { - t.Errorf("%d. `%v`: expected result upper type %v, got %v", i, tt.src, string(tt.result.UpperType), string(r.UpperType)) - } - - if bytes.Compare(r.Lower, tt.result.Lower) != 0 { - t.Errorf("%d. `%v`: expected result lower %v, got %v", i, tt.src, tt.result.Lower, r.Lower) - } - - if bytes.Compare(r.Upper, tt.result.Upper) != 0 { - t.Errorf("%d. `%v`: expected result upper %v, got %v", i, tt.src, tt.result.Upper, r.Upper) - } - } -} diff --git a/pgtype/record.go b/pgtype/record.go deleted file mode 100644 index 60733016..00000000 --- a/pgtype/record.go +++ /dev/null @@ -1,130 +0,0 @@ -package pgtype - -import ( - "encoding/binary" - "reflect" - - errors "golang.org/x/xerrors" -) - -// Record is the generic PostgreSQL record type such as is created with the -// "row" function. Record only implements BinaryEncoder and Value. The text -// format output format from PostgreSQL does not include type information and is -// therefore impossible to decode. No encoders are implemented because -// PostgreSQL does not support input of generic records. -type Record struct { - Fields []Value - Status Status -} - -func (dst *Record) Set(src interface{}) error { - if src == nil { - *dst = Record{Status: Null} - return nil - } - - switch value := src.(type) { - case []Value: - *dst = Record{Fields: value, Status: Present} - default: - return errors.Errorf("cannot convert %v to Record", src) - } - - return nil -} - -func (dst *Record) Get() interface{} { - switch dst.Status { - case Present: - return dst.Fields - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Record) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *[]Value: - *v = make([]Value, len(src.Fields)) - copy(*v, src.Fields) - return nil - case *[]interface{}: - *v = make([]interface{}, len(src.Fields)) - for i := range *v { - (*v)[i] = src.Fields[i].Get() - } - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Record) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Record{Status: Null} - return nil - } - - rp := 0 - - if len(src[rp:]) < 4 { - return errors.Errorf("Record incomplete %v", src) - } - fieldCount := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - - fields := make([]Value, fieldCount) - - for i := 0; i < fieldCount; i++ { - if len(src[rp:]) < 8 { - return errors.Errorf("Record incomplete %v", src) - } - fieldOID := OID(binary.BigEndian.Uint32(src[rp:])) - rp += 4 - - fieldLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - - var binaryDecoder BinaryDecoder - if dt, ok := ci.DataTypeForOID(fieldOID); ok { - binaryDecoder, _ = dt.Value.(BinaryDecoder) - } - if binaryDecoder == nil { - return errors.Errorf("unknown oid while decoding record: %v", fieldOID) - } - - var fieldBytes []byte - if fieldLen >= 0 { - if len(src[rp:]) < fieldLen { - return errors.Errorf("Record incomplete %v", src) - } - fieldBytes = src[rp : rp+fieldLen] - rp += fieldLen - } - - // Duplicate struct to scan into - binaryDecoder = reflect.New(reflect.ValueOf(binaryDecoder).Elem().Type()).Interface().(BinaryDecoder) - - if err := binaryDecoder.DecodeBinary(ci, fieldBytes); err != nil { - return err - } - - fields[i] = binaryDecoder.(Value) - } - - *dst = Record{Fields: fields, Status: Present} - - return nil -} diff --git a/pgtype/record_test.go b/pgtype/record_test.go deleted file mode 100644 index 5de8af31..00000000 --- a/pgtype/record_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package pgtype_test - -import ( - "context" - "fmt" - "reflect" - "testing" - - "github.com/jackc/pgx/v4" - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestRecordTranscode(t *testing.T) { - conn := testutil.MustConnectPgx(t) - defer testutil.MustCloseContext(t, conn) - - tests := []struct { - sql string - expected pgtype.Record - }{ - { - sql: `select row()`, - expected: pgtype.Record{ - Fields: []pgtype.Value{}, - Status: pgtype.Present, - }, - }, - { - sql: `select row('foo'::text, 42::int4)`, - expected: pgtype.Record{ - Fields: []pgtype.Value{ - &pgtype.Text{String: "foo", Status: pgtype.Present}, - &pgtype.Int4{Int: 42, Status: pgtype.Present}, - }, - Status: pgtype.Present, - }, - }, - { - sql: `select row(100.0::float4, 1.09::float4)`, - expected: pgtype.Record{ - Fields: []pgtype.Value{ - &pgtype.Float4{Float: 100, Status: pgtype.Present}, - &pgtype.Float4{Float: 1.09, Status: pgtype.Present}, - }, - Status: pgtype.Present, - }, - }, - { - sql: `select row('foo'::text, array[1, 2, null, 4]::int4[], 42::int4)`, - expected: pgtype.Record{ - Fields: []pgtype.Value{ - &pgtype.Text{String: "foo", Status: pgtype.Present}, - &pgtype.Int4Array{ - Elements: []pgtype.Int4{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Int: 4, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 4, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.Int4{Int: 42, Status: pgtype.Present}, - }, - Status: pgtype.Present, - }, - }, - { - sql: `select row(null)`, - expected: pgtype.Record{ - Fields: []pgtype.Value{ - &pgtype.Unknown{Status: pgtype.Null}, - }, - Status: pgtype.Present, - }, - }, - { - sql: `select null::record`, - expected: pgtype.Record{ - Status: pgtype.Null, - }, - }, - } - - for i, tt := range tests { - psName := fmt.Sprintf("test%d", i) - ps, err := conn.Prepare(context.Background(), psName, tt.sql) - if err != nil { - t.Fatal(err) - } - ps.FieldDescriptions[0].FormatCode = pgx.BinaryFormatCode - - var result pgtype.Record - if err := conn.QueryRow(context.Background(), psName).Scan(&result); err != nil { - t.Errorf("%d: %v", i, err) - continue - } - - if !reflect.DeepEqual(tt.expected, result) { - t.Errorf("%d: expected %#v, got %#v", i, tt.expected, result) - } - } -} - -func TestRecordWithUnknownOID(t *testing.T) { - conn := testutil.MustConnectPgx(t) - defer testutil.MustCloseContext(t, conn) - - _, err := conn.Exec(context.Background(), `drop type if exists floatrange; - -create type floatrange as range ( - subtype = float8, - subtype_diff = float8mi -);`) - if err != nil { - t.Fatal(err) - } - defer conn.Exec(context.Background(), "drop type floatrange") - - var result pgtype.Record - err = conn.QueryRow(context.Background(), "select row('foo'::text, floatrange(1, 10), 'bar'::text)").Scan(&result) - if err == nil { - t.Errorf("expected error but none") - } -} - -func TestRecordAssignTo(t *testing.T) { - var valueSlice []pgtype.Value - var interfaceSlice []interface{} - - simpleTests := []struct { - src pgtype.Record - dst interface{} - expected interface{} - }{ - { - src: pgtype.Record{ - Fields: []pgtype.Value{ - &pgtype.Text{String: "foo", Status: pgtype.Present}, - &pgtype.Int4{Int: 42, Status: pgtype.Present}, - }, - Status: pgtype.Present, - }, - dst: &valueSlice, - expected: []pgtype.Value{ - &pgtype.Text{String: "foo", Status: pgtype.Present}, - &pgtype.Int4{Int: 42, Status: pgtype.Present}, - }, - }, - { - src: pgtype.Record{ - Fields: []pgtype.Value{ - &pgtype.Text{String: "foo", Status: pgtype.Present}, - &pgtype.Int4{Int: 42, Status: pgtype.Present}, - }, - Status: pgtype.Present, - }, - dst: &interfaceSlice, - expected: []interface{}{"foo", int32(42)}, - }, - { - src: pgtype.Record{Status: pgtype.Null}, - dst: &valueSlice, - expected: (([]pgtype.Value)(nil)), - }, - { - src: pgtype.Record{Status: pgtype.Null}, - dst: &interfaceSlice, - expected: (([]interface{})(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } -} diff --git a/pgtype/testutil/testutil.go b/pgtype/testutil/testutil.go deleted file mode 100644 index 121eb754..00000000 --- a/pgtype/testutil/testutil.go +++ /dev/null @@ -1,286 +0,0 @@ -package testutil - -import ( - "context" - "database/sql" - "fmt" - "os" - "reflect" - "testing" - - "github.com/jackc/pgx/v4" - "github.com/jackc/pgx/v4/pgtype" - _ "github.com/jackc/pgx/v4/stdlib" - _ "github.com/lib/pq" -) - -func MustConnectDatabaseSQL(t testing.TB, driverName string) *sql.DB { - var sqlDriverName string - switch driverName { - case "github.com/lib/pq": - sqlDriverName = "postgres" - case "github.com/jackc/pgx/stdlib": - sqlDriverName = "pgx" - default: - t.Fatalf("Unknown driver %v", driverName) - } - - db, err := sql.Open(sqlDriverName, os.Getenv("PGX_TEST_DATABASE")) - if err != nil { - t.Fatal(err) - } - - return db -} - -func MustConnectPgx(t testing.TB) *pgx.Conn { - conn, err := pgx.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE")) - if err != nil { - t.Fatal(err) - } - - return conn -} - -func MustClose(t testing.TB, conn interface { - Close() error -}) { - err := conn.Close() - if err != nil { - t.Fatal(err) - } -} - -func MustCloseContext(t testing.TB, conn interface { - Close(context.Context) error -}) { - err := conn.Close(context.Background()) - if err != nil { - t.Fatal(err) - } -} - -type forceTextEncoder struct { - e pgtype.TextEncoder -} - -func (f forceTextEncoder) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { - return f.e.EncodeText(ci, buf) -} - -type forceBinaryEncoder struct { - e pgtype.BinaryEncoder -} - -func (f forceBinaryEncoder) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { - return f.e.EncodeBinary(ci, buf) -} - -func ForceEncoder(e interface{}, formatCode int16) interface{} { - switch formatCode { - case pgx.TextFormatCode: - if e, ok := e.(pgtype.TextEncoder); ok { - return forceTextEncoder{e: e} - } - case pgx.BinaryFormatCode: - if e, ok := e.(pgtype.BinaryEncoder); ok { - return forceBinaryEncoder{e: e.(pgtype.BinaryEncoder)} - } - } - return nil -} - -func TestSuccessfulTranscode(t testing.TB, pgTypeName string, values []interface{}) { - TestSuccessfulTranscodeEqFunc(t, pgTypeName, values, func(a, b interface{}) bool { - return reflect.DeepEqual(a, b) - }) -} - -func TestSuccessfulTranscodeEqFunc(t testing.TB, pgTypeName string, values []interface{}, eqFunc func(a, b interface{}) bool) { - TestPgxSuccessfulTranscodeEqFunc(t, pgTypeName, values, eqFunc) - for _, driverName := range []string{"github.com/lib/pq", "github.com/jackc/pgx/stdlib"} { - TestDatabaseSQLSuccessfulTranscodeEqFunc(t, driverName, pgTypeName, values, eqFunc) - } -} - -func TestPgxSuccessfulTranscodeEqFunc(t testing.TB, pgTypeName string, values []interface{}, eqFunc func(a, b interface{}) bool) { - conn := MustConnectPgx(t) - defer MustCloseContext(t, conn) - - _, err := conn.Prepare(context.Background(), "test", fmt.Sprintf("select $1::%s", pgTypeName)) - if err != nil { - t.Fatal(err) - } - - formats := []struct { - name string - formatCode int16 - }{ - {name: "TextFormat", formatCode: pgx.TextFormatCode}, - {name: "BinaryFormat", formatCode: pgx.BinaryFormatCode}, - } - - for i, v := range values { - for _, paramFormat := range formats { - for _, resultFormat := range formats { - vEncoder := ForceEncoder(v, paramFormat.formatCode) - if vEncoder == nil { - t.Logf("Skipping Param %s Result %s: %#v does not implement %v for encoding", paramFormat.name, resultFormat.name, v, paramFormat.name) - continue - } - switch resultFormat.formatCode { - case pgx.TextFormatCode: - if _, ok := v.(pgtype.TextEncoder); !ok { - t.Logf("Skipping Param %s Result %s: %#v does not implement %v for decoding", paramFormat.name, resultFormat.name, v, resultFormat.name) - continue - } - case pgx.BinaryFormatCode: - if _, ok := v.(pgtype.BinaryEncoder); !ok { - t.Logf("Skipping Param %s Result %s: %#v does not implement %v for decoding", paramFormat.name, resultFormat.name, v, resultFormat.name) - continue - } - } - - // Derefence value if it is a pointer - derefV := v - refVal := reflect.ValueOf(v) - if refVal.Kind() == reflect.Ptr { - derefV = refVal.Elem().Interface() - } - - result := reflect.New(reflect.TypeOf(derefV)) - - err := conn.QueryRow(context.Background(), "test", pgx.QueryResultFormats{resultFormat.formatCode}, vEncoder).Scan(result.Interface()) - if err != nil { - t.Errorf("Param %s Result %s %d: %v", paramFormat.name, resultFormat.name, i, err) - } - - if !eqFunc(result.Elem().Interface(), derefV) { - t.Errorf("Param %s Result %s %d: expected %v, got %v", paramFormat.name, resultFormat.name, i, derefV, result.Elem().Interface()) - } - } - } - } -} - -func TestDatabaseSQLSuccessfulTranscodeEqFunc(t testing.TB, driverName, pgTypeName string, values []interface{}, eqFunc func(a, b interface{}) bool) { - conn := MustConnectDatabaseSQL(t, driverName) - defer MustClose(t, conn) - - ps, err := conn.Prepare(fmt.Sprintf("select $1::%s", pgTypeName)) - if err != nil { - t.Fatal(err) - } - - for i, v := range values { - // Derefence value if it is a pointer - derefV := v - refVal := reflect.ValueOf(v) - if refVal.Kind() == reflect.Ptr { - derefV = refVal.Elem().Interface() - } - - result := reflect.New(reflect.TypeOf(derefV)) - err := ps.QueryRow(v).Scan(result.Interface()) - if err != nil { - t.Errorf("%v %d: %v", driverName, i, err) - } - - if !eqFunc(result.Elem().Interface(), derefV) { - t.Errorf("%v %d: expected %v, got %v", driverName, i, derefV, result.Elem().Interface()) - } - } -} - -type NormalizeTest struct { - SQL string - Value interface{} -} - -func TestSuccessfulNormalize(t testing.TB, tests []NormalizeTest) { - TestSuccessfulNormalizeEqFunc(t, tests, func(a, b interface{}) bool { - return reflect.DeepEqual(a, b) - }) -} - -func TestSuccessfulNormalizeEqFunc(t testing.TB, tests []NormalizeTest, eqFunc func(a, b interface{}) bool) { - TestPgxSuccessfulNormalizeEqFunc(t, tests, eqFunc) - for _, driverName := range []string{"github.com/lib/pq", "github.com/jackc/pgx/stdlib"} { - TestDatabaseSQLSuccessfulNormalizeEqFunc(t, driverName, tests, eqFunc) - } -} - -func TestPgxSuccessfulNormalizeEqFunc(t testing.TB, tests []NormalizeTest, eqFunc func(a, b interface{}) bool) { - conn := MustConnectPgx(t) - defer MustCloseContext(t, conn) - - formats := []struct { - name string - formatCode int16 - }{ - {name: "TextFormat", formatCode: pgx.TextFormatCode}, - {name: "BinaryFormat", formatCode: pgx.BinaryFormatCode}, - } - - for i, tt := range tests { - for _, fc := range formats { - psName := fmt.Sprintf("test%d", i) - ps, err := conn.Prepare(context.Background(), psName, tt.SQL) - if err != nil { - t.Fatal(err) - } - - ps.FieldDescriptions[0].FormatCode = fc.formatCode - if ForceEncoder(tt.Value, fc.formatCode) == nil { - t.Logf("Skipping: %#v does not implement %v", tt.Value, fc.name) - continue - } - // Derefence value if it is a pointer - derefV := tt.Value - refVal := reflect.ValueOf(tt.Value) - if refVal.Kind() == reflect.Ptr { - derefV = refVal.Elem().Interface() - } - - result := reflect.New(reflect.TypeOf(derefV)) - err = conn.QueryRow(context.Background(), psName).Scan(result.Interface()) - if err != nil { - t.Errorf("%v %d: %v", fc.name, i, err) - } - - if !eqFunc(result.Elem().Interface(), derefV) { - t.Errorf("%v %d: expected %v, got %v", fc.name, i, derefV, result.Elem().Interface()) - } - } - } -} - -func TestDatabaseSQLSuccessfulNormalizeEqFunc(t testing.TB, driverName string, tests []NormalizeTest, eqFunc func(a, b interface{}) bool) { - conn := MustConnectDatabaseSQL(t, driverName) - defer MustClose(t, conn) - - for i, tt := range tests { - ps, err := conn.Prepare(tt.SQL) - if err != nil { - t.Errorf("%d. %v", i, err) - continue - } - - // Derefence value if it is a pointer - derefV := tt.Value - refVal := reflect.ValueOf(tt.Value) - if refVal.Kind() == reflect.Ptr { - derefV = refVal.Elem().Interface() - } - - result := reflect.New(reflect.TypeOf(derefV)) - err = ps.QueryRow().Scan(result.Interface()) - if err != nil { - t.Errorf("%v %d: %v", driverName, i, err) - } - - if !eqFunc(result.Elem().Interface(), derefV) { - t.Errorf("%v %d: expected %v, got %v", driverName, i, derefV, result.Elem().Interface()) - } - } -} diff --git a/pgtype/text.go b/pgtype/text.go deleted file mode 100644 index 4d4e6bb4..00000000 --- a/pgtype/text.go +++ /dev/null @@ -1,164 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/json" - - errors "golang.org/x/xerrors" -) - -type Text struct { - String string - Status Status -} - -func (dst *Text) Set(src interface{}) error { - if src == nil { - *dst = Text{Status: Null} - return nil - } - - switch value := src.(type) { - case string: - *dst = Text{String: value, Status: Present} - case *string: - if value == nil { - *dst = Text{Status: Null} - } else { - *dst = Text{String: *value, Status: Present} - } - case []byte: - if value == nil { - *dst = Text{Status: Null} - } else { - *dst = Text{String: string(value), Status: Present} - } - default: - if originalSrc, ok := underlyingStringType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Text", value) - } - - return nil -} - -func (dst *Text) Get() interface{} { - switch dst.Status { - case Present: - return dst.String - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Text) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *string: - *v = src.String - return nil - case *[]byte: - *v = make([]byte, len(src.String)) - copy(*v, src.String) - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Text) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Text{Status: Null} - return nil - } - - *dst = Text{String: string(src), Status: Present} - return nil -} - -func (dst *Text) DecodeBinary(ci *ConnInfo, src []byte) error { - return dst.DecodeText(ci, src) -} - -func (src *Text) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, src.String...), nil -} - -func (src *Text) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - return src.EncodeText(ci, buf) -} - -// Scan implements the database/sql Scanner interface. -func (dst *Text) Scan(src interface{}) error { - if src == nil { - *dst = Text{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Text) Value() (driver.Value, error) { - switch src.Status { - case Present: - return src.String, nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} - -func (src *Text) MarshalJSON() ([]byte, error) { - switch src.Status { - case Present: - return json.Marshal(src.String) - case Null: - return []byte("null"), nil - case Undefined: - return nil, errUndefined - } - - return nil, errBadStatus -} - -func (dst *Text) UnmarshalJSON(b []byte) error { - var s string - err := json.Unmarshal(b, &s) - if err != nil { - return err - } - - *dst = Text{String: s, Status: Present} - - return nil -} diff --git a/pgtype/text_array.go b/pgtype/text_array.go deleted file mode 100644 index b590972e..00000000 --- a/pgtype/text_array.go +++ /dev/null @@ -1,301 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type TextArray struct { - Elements []Text - Dimensions []ArrayDimension - Status Status -} - -func (dst *TextArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = TextArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []string: - if value == nil { - *dst = TextArray{Status: Null} - } else if len(value) == 0 { - *dst = TextArray{Status: Present} - } else { - elements := make([]Text, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = TextArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to TextArray", value) - } - - return nil -} - -func (dst *TextArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *TextArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]string: - *v = make([]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *TextArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = TextArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Text - - if len(uta.Elements) > 0 { - elements = make([]Text, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Text - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = TextArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *TextArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = TextArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = TextArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Text, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = TextArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *TextArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *TextArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("text"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "text") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *TextArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *TextArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/text_array_test.go b/pgtype/text_array_test.go deleted file mode 100644 index b03312d9..00000000 --- a/pgtype/text_array_test.go +++ /dev/null @@ -1,152 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestTextArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "text[]", []interface{}{ - &pgtype.TextArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.TextArray{ - Elements: []pgtype.Text{ - {String: "foo", Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.TextArray{Status: pgtype.Null}, - &pgtype.TextArray{ - Elements: []pgtype.Text{ - {String: "bar ", Status: pgtype.Present}, - {String: "NuLL", Status: pgtype.Present}, - {String: `wow"quz\`, Status: pgtype.Present}, - {String: "", Status: pgtype.Present}, - {Status: pgtype.Null}, - {String: "null", Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.TextArray{ - Elements: []pgtype.Text{ - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "quz", Status: pgtype.Present}, - {String: "foo", Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestTextArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.TextArray - }{ - { - source: []string{"foo"}, - result: pgtype.TextArray{ - Elements: []pgtype.Text{{String: "foo", Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]string)(nil)), - result: pgtype.TextArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.TextArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestTextArrayAssignTo(t *testing.T) { - var stringSlice []string - type _stringSlice []string - var namedStringSlice _stringSlice - - simpleTests := []struct { - src pgtype.TextArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.TextArray{ - Elements: []pgtype.Text{{String: "foo", Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &stringSlice, - expected: []string{"foo"}, - }, - { - src: pgtype.TextArray{ - Elements: []pgtype.Text{{String: "bar", Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &namedStringSlice, - expected: _stringSlice{"bar"}, - }, - { - src: pgtype.TextArray{Status: pgtype.Null}, - dst: &stringSlice, - expected: (([]string)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.TextArray - dst interface{} - }{ - { - src: pgtype.TextArray{ - Elements: []pgtype.Text{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &stringSlice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/text_test.go b/pgtype/text_test.go deleted file mode 100644 index 53f4bd7e..00000000 --- a/pgtype/text_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package pgtype_test - -import ( - "bytes" - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestTextTranscode(t *testing.T) { - for _, pgTypeName := range []string{"text", "varchar"} { - testutil.TestSuccessfulTranscode(t, pgTypeName, []interface{}{ - &pgtype.Text{String: "", Status: pgtype.Present}, - &pgtype.Text{String: "foo", Status: pgtype.Present}, - &pgtype.Text{Status: pgtype.Null}, - }) - } -} - -func TestTextSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.Text - }{ - {source: "foo", result: pgtype.Text{String: "foo", Status: pgtype.Present}}, - {source: _string("bar"), result: pgtype.Text{String: "bar", Status: pgtype.Present}}, - {source: (*string)(nil), result: pgtype.Text{Status: pgtype.Null}}, - } - - for i, tt := range successfulTests { - var d pgtype.Text - err := d.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if d != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, d) - } - } -} - -func TestTextAssignTo(t *testing.T) { - var s string - var ps *string - - stringTests := []struct { - src pgtype.Text - dst interface{} - expected interface{} - }{ - {src: pgtype.Text{String: "foo", Status: pgtype.Present}, dst: &s, expected: "foo"}, - {src: pgtype.Text{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))}, - } - - for i, tt := range stringTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - var buf []byte - - bytesTests := []struct { - src pgtype.Text - dst *[]byte - expected []byte - }{ - {src: pgtype.Text{String: "foo", Status: pgtype.Present}, dst: &buf, expected: []byte("foo")}, - {src: pgtype.Text{Status: pgtype.Null}, dst: &buf, expected: nil}, - } - - for i, tt := range bytesTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if bytes.Compare(*tt.dst, tt.expected) != 0 { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, tt.dst) - } - } - - pointerAllocTests := []struct { - src pgtype.Text - dst interface{} - expected interface{} - }{ - {src: pgtype.Text{String: "foo", Status: pgtype.Present}, dst: &ps, expected: "foo"}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Text - dst interface{} - }{ - {src: pgtype.Text{Status: pgtype.Null}, dst: &s}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/tid.go b/pgtype/tid.go deleted file mode 100644 index ff788b84..00000000 --- a/pgtype/tid.go +++ /dev/null @@ -1,144 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "fmt" - "strconv" - "strings" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -// TID is PostgreSQL's Tuple Identifier type. -// -// When one does -// -// select ctid, * from some_table; -// -// it is the data type of the ctid hidden system column. -// -// It is currently implemented as a pair unsigned two byte integers. -// Its conversion functions can be found in src/backend/utils/adt/tid.c -// in the PostgreSQL sources. -type TID struct { - BlockNumber uint32 - OffsetNumber uint16 - Status Status -} - -func (dst *TID) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to TID", src) -} - -func (dst *TID) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *TID) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *TID) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = TID{Status: Null} - return nil - } - - if len(src) < 5 { - return errors.Errorf("invalid length for tid: %v", len(src)) - } - - parts := strings.SplitN(string(src[1:len(src)-1]), ",", 2) - if len(parts) < 2 { - return errors.Errorf("invalid format for tid") - } - - blockNumber, err := strconv.ParseUint(parts[0], 10, 32) - if err != nil { - return err - } - - offsetNumber, err := strconv.ParseUint(parts[1], 10, 16) - if err != nil { - return err - } - - *dst = TID{BlockNumber: uint32(blockNumber), OffsetNumber: uint16(offsetNumber), Status: Present} - return nil -} - -func (dst *TID) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = TID{Status: Null} - return nil - } - - if len(src) != 6 { - return errors.Errorf("invalid length for tid: %v", len(src)) - } - - *dst = TID{ - BlockNumber: binary.BigEndian.Uint32(src), - OffsetNumber: binary.BigEndian.Uint16(src[4:]), - Status: Present, - } - return nil -} - -func (src *TID) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = append(buf, fmt.Sprintf(`(%d,%d)`, src.BlockNumber, src.OffsetNumber)...) - return buf, nil -} - -func (src *TID) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = pgio.AppendUint32(buf, src.BlockNumber) - buf = pgio.AppendUint16(buf, src.OffsetNumber) - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *TID) Scan(src interface{}) error { - if src == nil { - *dst = TID{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *TID) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/tid_test.go b/pgtype/tid_test.go deleted file mode 100644 index cd753ab4..00000000 --- a/pgtype/tid_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package pgtype_test - -import ( - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestTIDTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "tid", []interface{}{ - &pgtype.TID{BlockNumber: 42, OffsetNumber: 43, Status: pgtype.Present}, - &pgtype.TID{BlockNumber: 4294967295, OffsetNumber: 65535, Status: pgtype.Present}, - &pgtype.TID{Status: pgtype.Null}, - }) -} diff --git a/pgtype/timestamp.go b/pgtype/timestamp.go deleted file mode 100644 index 40dfdac8..00000000 --- a/pgtype/timestamp.go +++ /dev/null @@ -1,226 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "time" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -const pgTimestampFormat = "2006-01-02 15:04:05.999999999" - -// Timestamp represents the PostgreSQL timestamp type. The PostgreSQL -// timestamp does not have a time zone. This presents a problem when -// translating to and from time.Time which requires a time zone. It is highly -// recommended to use timestamptz whenever possible. Timestamp methods either -// convert to UTC or return an error on non-UTC times. -type Timestamp struct { - Time time.Time // Time must always be in UTC. - Status Status - InfinityModifier InfinityModifier -} - -// Set converts src into a Timestamp and stores in dst. If src is a -// time.Time in a non-UTC time zone, the time zone is discarded. -func (dst *Timestamp) Set(src interface{}) error { - if src == nil { - *dst = Timestamp{Status: Null} - return nil - } - - switch value := src.(type) { - case time.Time: - *dst = Timestamp{Time: time.Date(value.Year(), value.Month(), value.Day(), value.Hour(), value.Minute(), value.Second(), value.Nanosecond(), time.UTC), Status: Present} - default: - if originalSrc, ok := underlyingTimeType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Timestamp", value) - } - - return nil -} - -func (dst *Timestamp) Get() interface{} { - switch dst.Status { - case Present: - if dst.InfinityModifier != None { - return dst.InfinityModifier - } - return dst.Time - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Timestamp) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *time.Time: - if src.InfinityModifier != None { - return errors.Errorf("cannot assign %v to %T", src, dst) - } - *v = src.Time - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -// DecodeText decodes from src into dst. The decoded time is considered to -// be in UTC. -func (dst *Timestamp) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Timestamp{Status: Null} - return nil - } - - sbuf := string(src) - switch sbuf { - case "infinity": - *dst = Timestamp{Status: Present, InfinityModifier: Infinity} - case "-infinity": - *dst = Timestamp{Status: Present, InfinityModifier: -Infinity} - default: - tim, err := time.Parse(pgTimestampFormat, sbuf) - if err != nil { - return err - } - - *dst = Timestamp{Time: tim, Status: Present} - } - - return nil -} - -// DecodeBinary decodes from src into dst. The decoded time is considered to -// be in UTC. -func (dst *Timestamp) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Timestamp{Status: Null} - return nil - } - - if len(src) != 8 { - return errors.Errorf("invalid length for timestamp: %v", len(src)) - } - - microsecSinceY2K := int64(binary.BigEndian.Uint64(src)) - - switch microsecSinceY2K { - case infinityMicrosecondOffset: - *dst = Timestamp{Status: Present, InfinityModifier: Infinity} - case negativeInfinityMicrosecondOffset: - *dst = Timestamp{Status: Present, InfinityModifier: -Infinity} - default: - microsecSinceUnixEpoch := microsecFromUnixEpochToY2K + microsecSinceY2K - tim := time.Unix(microsecSinceUnixEpoch/1000000, (microsecSinceUnixEpoch%1000000)*1000).UTC() - *dst = Timestamp{Time: tim, Status: Present} - } - - return nil -} - -// EncodeText writes the text encoding of src into w. If src.Time is not in -// the UTC time zone it returns an error. -func (src *Timestamp) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - if src.Time.Location() != time.UTC { - return nil, errors.Errorf("cannot encode non-UTC time into timestamp") - } - - var s string - - switch src.InfinityModifier { - case None: - s = src.Time.Format(pgTimestampFormat) - case Infinity: - s = "infinity" - case NegativeInfinity: - s = "-infinity" - } - - return append(buf, s...), nil -} - -// EncodeBinary writes the binary encoding of src into w. If src.Time is not in -// the UTC time zone it returns an error. -func (src *Timestamp) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - if src.Time.Location() != time.UTC { - return nil, errors.Errorf("cannot encode non-UTC time into timestamp") - } - - var microsecSinceY2K int64 - switch src.InfinityModifier { - case None: - microsecSinceUnixEpoch := src.Time.Unix()*1000000 + int64(src.Time.Nanosecond())/1000 - microsecSinceY2K = microsecSinceUnixEpoch - microsecFromUnixEpochToY2K - case Infinity: - microsecSinceY2K = infinityMicrosecondOffset - case NegativeInfinity: - microsecSinceY2K = negativeInfinityMicrosecondOffset - } - - return pgio.AppendInt64(buf, microsecSinceY2K), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Timestamp) Scan(src interface{}) error { - if src == nil { - *dst = Timestamp{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - case time.Time: - *dst = Timestamp{Time: src, Status: Present} - return nil - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Timestamp) Value() (driver.Value, error) { - switch src.Status { - case Present: - if src.InfinityModifier != None { - return src.InfinityModifier.String(), nil - } - return src.Time, nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} diff --git a/pgtype/timestamp_array.go b/pgtype/timestamp_array.go deleted file mode 100644 index 95f76639..00000000 --- a/pgtype/timestamp_array.go +++ /dev/null @@ -1,302 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "time" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type TimestampArray struct { - Elements []Timestamp - Dimensions []ArrayDimension - Status Status -} - -func (dst *TimestampArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = TimestampArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []time.Time: - if value == nil { - *dst = TimestampArray{Status: Null} - } else if len(value) == 0 { - *dst = TimestampArray{Status: Present} - } else { - elements := make([]Timestamp, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = TimestampArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to TimestampArray", value) - } - - return nil -} - -func (dst *TimestampArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *TimestampArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]time.Time: - *v = make([]time.Time, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *TimestampArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = TimestampArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Timestamp - - if len(uta.Elements) > 0 { - elements = make([]Timestamp, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Timestamp - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = TimestampArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *TimestampArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = TimestampArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = TimestampArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Timestamp, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = TimestampArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *TimestampArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *TimestampArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("timestamp"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "timestamp") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *TimestampArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *TimestampArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/timestamp_array_test.go b/pgtype/timestamp_array_test.go deleted file mode 100644 index 002d1ca4..00000000 --- a/pgtype/timestamp_array_test.go +++ /dev/null @@ -1,159 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - "time" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestTimestampArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscodeEqFunc(t, "timestamp[]", []interface{}{ - &pgtype.TimestampArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.TimestampArray{ - Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.TimestampArray{Status: pgtype.Null}, - &pgtype.TimestampArray{ - Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Status: pgtype.Null}, - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.TimestampArray{ - Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 3, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }, func(a, b interface{}) bool { - ata := a.(pgtype.TimestampArray) - bta := b.(pgtype.TimestampArray) - - if len(ata.Elements) != len(bta.Elements) || ata.Status != bta.Status { - return false - } - - for i := range ata.Elements { - ae, be := ata.Elements[i], bta.Elements[i] - if !(ae.Time.Equal(be.Time) && ae.Status == be.Status && ae.InfinityModifier == be.InfinityModifier) { - return false - } - } - - return true - }) -} - -func TestTimestampArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.TimestampArray - }{ - { - source: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, - result: pgtype.TimestampArray{ - Elements: []pgtype.Timestamp{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]time.Time)(nil)), - result: pgtype.TimestampArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.TimestampArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestTimestampArrayAssignTo(t *testing.T) { - var timeSlice []time.Time - - simpleTests := []struct { - src pgtype.TimestampArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.TimestampArray{ - Elements: []pgtype.Timestamp{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &timeSlice, - expected: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, - }, - { - src: pgtype.TimestampArray{Status: pgtype.Null}, - dst: &timeSlice, - expected: (([]time.Time)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.TimestampArray - dst interface{} - }{ - { - src: pgtype.TimestampArray{ - Elements: []pgtype.Timestamp{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &timeSlice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } - -} diff --git a/pgtype/timestamp_test.go b/pgtype/timestamp_test.go deleted file mode 100644 index 732f3cc2..00000000 --- a/pgtype/timestamp_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - "time" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestTimestampTranscode(t *testing.T) { - testutil.TestSuccessfulTranscodeEqFunc(t, "timestamp", []interface{}{ - &pgtype.Timestamp{Time: time.Date(1800, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(1905, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(1940, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(1960, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Status: pgtype.Null}, - &pgtype.Timestamp{Status: pgtype.Present, InfinityModifier: pgtype.Infinity}, - &pgtype.Timestamp{Status: pgtype.Present, InfinityModifier: -pgtype.Infinity}, - }, func(a, b interface{}) bool { - at := a.(pgtype.Timestamp) - bt := b.(pgtype.Timestamp) - - return at.Time.Equal(bt.Time) && at.Status == bt.Status && at.InfinityModifier == bt.InfinityModifier - }) -} - -func TestTimestampSet(t *testing.T) { - type _time time.Time - - successfulTests := []struct { - source interface{} - result pgtype.Timestamp - }{ - {source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(1999, 12, 31, 12, 59, 59, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(1999, 12, 31, 12, 59, 59, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2000, 1, 1, 0, 0, 1, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(2000, 1, 1, 0, 0, 1, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)), result: pgtype.Timestamp{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var r pgtype.Timestamp - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestTimestampAssignTo(t *testing.T) { - var tim time.Time - var ptim *time.Time - - simpleTests := []struct { - src pgtype.Timestamp - dst interface{} - expected interface{} - }{ - {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, dst: &tim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, - {src: pgtype.Timestamp{Time: time.Time{}, Status: pgtype.Null}, dst: &ptim, expected: ((*time.Time)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.Timestamp - dst interface{} - expected interface{} - }{ - {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &ptim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Timestamp - dst interface{} - }{ - {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.Infinity, Status: pgtype.Present}, dst: &tim}, - {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}, dst: &tim}, - {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Null}, dst: &tim}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/timestamptz.go b/pgtype/timestamptz.go deleted file mode 100644 index 752c1818..00000000 --- a/pgtype/timestamptz.go +++ /dev/null @@ -1,222 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "time" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -const pgTimestamptzHourFormat = "2006-01-02 15:04:05.999999999Z07" -const pgTimestamptzMinuteFormat = "2006-01-02 15:04:05.999999999Z07:00" -const pgTimestamptzSecondFormat = "2006-01-02 15:04:05.999999999Z07:00:00" -const microsecFromUnixEpochToY2K = 946684800 * 1000000 - -const ( - negativeInfinityMicrosecondOffset = -9223372036854775808 - infinityMicrosecondOffset = 9223372036854775807 -) - -type Timestamptz struct { - Time time.Time - Status Status - InfinityModifier InfinityModifier -} - -func (dst *Timestamptz) Set(src interface{}) error { - if src == nil { - *dst = Timestamptz{Status: Null} - return nil - } - - switch value := src.(type) { - case time.Time: - *dst = Timestamptz{Time: value, Status: Present} - default: - if originalSrc, ok := underlyingTimeType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Timestamptz", value) - } - - return nil -} - -func (dst *Timestamptz) Get() interface{} { - switch dst.Status { - case Present: - if dst.InfinityModifier != None { - return dst.InfinityModifier - } - return dst.Time - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Timestamptz) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *time.Time: - if src.InfinityModifier != None { - return errors.Errorf("cannot assign %v to %T", src, dst) - } - *v = src.Time - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *Timestamptz) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Timestamptz{Status: Null} - return nil - } - - sbuf := string(src) - switch sbuf { - case "infinity": - *dst = Timestamptz{Status: Present, InfinityModifier: Infinity} - case "-infinity": - *dst = Timestamptz{Status: Present, InfinityModifier: -Infinity} - default: - var format string - if sbuf[len(sbuf)-9] == '-' || sbuf[len(sbuf)-9] == '+' { - format = pgTimestamptzSecondFormat - } else if sbuf[len(sbuf)-6] == '-' || sbuf[len(sbuf)-6] == '+' { - format = pgTimestamptzMinuteFormat - } else { - format = pgTimestamptzHourFormat - } - - tim, err := time.Parse(format, sbuf) - if err != nil { - return err - } - - *dst = Timestamptz{Time: tim, Status: Present} - } - - return nil -} - -func (dst *Timestamptz) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Timestamptz{Status: Null} - return nil - } - - if len(src) != 8 { - return errors.Errorf("invalid length for timestamptz: %v", len(src)) - } - - microsecSinceY2K := int64(binary.BigEndian.Uint64(src)) - - switch microsecSinceY2K { - case infinityMicrosecondOffset: - *dst = Timestamptz{Status: Present, InfinityModifier: Infinity} - case negativeInfinityMicrosecondOffset: - *dst = Timestamptz{Status: Present, InfinityModifier: -Infinity} - default: - microsecSinceUnixEpoch := microsecFromUnixEpochToY2K + microsecSinceY2K - tim := time.Unix(microsecSinceUnixEpoch/1000000, (microsecSinceUnixEpoch%1000000)*1000) - *dst = Timestamptz{Time: tim, Status: Present} - } - - return nil -} - -func (src *Timestamptz) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var s string - - switch src.InfinityModifier { - case None: - s = src.Time.UTC().Format(pgTimestamptzSecondFormat) - case Infinity: - s = "infinity" - case NegativeInfinity: - s = "-infinity" - } - - return append(buf, s...), nil -} - -func (src *Timestamptz) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var microsecSinceY2K int64 - switch src.InfinityModifier { - case None: - microsecSinceUnixEpoch := src.Time.Unix()*1000000 + int64(src.Time.Nanosecond())/1000 - microsecSinceY2K = microsecSinceUnixEpoch - microsecFromUnixEpochToY2K - case Infinity: - microsecSinceY2K = infinityMicrosecondOffset - case NegativeInfinity: - microsecSinceY2K = negativeInfinityMicrosecondOffset - } - - return pgio.AppendInt64(buf, microsecSinceY2K), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Timestamptz) Scan(src interface{}) error { - if src == nil { - *dst = Timestamptz{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - case time.Time: - *dst = Timestamptz{Time: src, Status: Present} - return nil - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Timestamptz) Value() (driver.Value, error) { - switch src.Status { - case Present: - if src.InfinityModifier != None { - return src.InfinityModifier.String(), nil - } - return src.Time, nil - case Null: - return nil, nil - default: - return nil, errUndefined - } -} diff --git a/pgtype/timestamptz_array.go b/pgtype/timestamptz_array.go deleted file mode 100644 index 7fe60d50..00000000 --- a/pgtype/timestamptz_array.go +++ /dev/null @@ -1,302 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - "time" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type TimestamptzArray struct { - Elements []Timestamptz - Dimensions []ArrayDimension - Status Status -} - -func (dst *TimestamptzArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = TimestamptzArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []time.Time: - if value == nil { - *dst = TimestamptzArray{Status: Null} - } else if len(value) == 0 { - *dst = TimestamptzArray{Status: Present} - } else { - elements := make([]Timestamptz, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = TimestamptzArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to TimestamptzArray", value) - } - - return nil -} - -func (dst *TimestamptzArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *TimestamptzArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]time.Time: - *v = make([]time.Time, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *TimestamptzArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = TimestamptzArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Timestamptz - - if len(uta.Elements) > 0 { - elements = make([]Timestamptz, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Timestamptz - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = TimestamptzArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *TimestamptzArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = TimestamptzArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = TimestamptzArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Timestamptz, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = TimestamptzArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *TimestamptzArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *TimestamptzArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("timestamptz"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "timestamptz") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *TimestamptzArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *TimestamptzArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/timestamptz_array_test.go b/pgtype/timestamptz_array_test.go deleted file mode 100644 index ac9975f0..00000000 --- a/pgtype/timestamptz_array_test.go +++ /dev/null @@ -1,159 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - "time" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestTimestamptzArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscodeEqFunc(t, "timestamptz[]", []interface{}{ - &pgtype.TimestamptzArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.TimestamptzArray{ - Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.TimestamptzArray{Status: pgtype.Null}, - &pgtype.TimestamptzArray{ - Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Status: pgtype.Null}, - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.TimestamptzArray{ - Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 3, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }, func(a, b interface{}) bool { - ata := a.(pgtype.TimestamptzArray) - bta := b.(pgtype.TimestamptzArray) - - if len(ata.Elements) != len(bta.Elements) || ata.Status != bta.Status { - return false - } - - for i := range ata.Elements { - ae, be := ata.Elements[i], bta.Elements[i] - if !(ae.Time.Equal(be.Time) && ae.Status == be.Status && ae.InfinityModifier == be.InfinityModifier) { - return false - } - } - - return true - }) -} - -func TestTimestamptzArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.TimestamptzArray - }{ - { - source: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, - result: pgtype.TimestamptzArray{ - Elements: []pgtype.Timestamptz{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]time.Time)(nil)), - result: pgtype.TimestamptzArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.TimestamptzArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestTimestamptzArrayAssignTo(t *testing.T) { - var timeSlice []time.Time - - simpleTests := []struct { - src pgtype.TimestamptzArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.TimestamptzArray{ - Elements: []pgtype.Timestamptz{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &timeSlice, - expected: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, - }, - { - src: pgtype.TimestamptzArray{Status: pgtype.Null}, - dst: &timeSlice, - expected: (([]time.Time)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.TimestamptzArray - dst interface{} - }{ - { - src: pgtype.TimestamptzArray{ - Elements: []pgtype.Timestamptz{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &timeSlice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } - -} diff --git a/pgtype/timestamptz_test.go b/pgtype/timestamptz_test.go deleted file mode 100644 index f522117b..00000000 --- a/pgtype/timestamptz_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - "time" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestTimestamptzTranscode(t *testing.T) { - testutil.TestSuccessfulTranscodeEqFunc(t, "timestamptz", []interface{}{ - &pgtype.Timestamptz{Time: time.Date(1800, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(1905, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(1940, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(1960, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Status: pgtype.Null}, - &pgtype.Timestamptz{Status: pgtype.Present, InfinityModifier: pgtype.Infinity}, - &pgtype.Timestamptz{Status: pgtype.Present, InfinityModifier: -pgtype.Infinity}, - }, func(a, b interface{}) bool { - at := a.(pgtype.Timestamptz) - bt := b.(pgtype.Timestamptz) - - return at.Time.Equal(bt.Time) && at.Status == bt.Status && at.InfinityModifier == bt.InfinityModifier - }) -} - -func TestTimestamptzSet(t *testing.T) { - type _time time.Time - - successfulTests := []struct { - source interface{} - result pgtype.Timestamptz - }{ - {source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}}, - {source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}}, - {source: time.Date(1999, 12, 31, 12, 59, 59, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(1999, 12, 31, 12, 59, 59, 0, time.Local), Status: pgtype.Present}}, - {source: time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}}, - {source: time.Date(2000, 1, 1, 0, 0, 1, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(2000, 1, 1, 0, 0, 1, 0, time.Local), Status: pgtype.Present}}, - {source: time.Date(2200, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}}, - {source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local)), result: pgtype.Timestamptz{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var r pgtype.Timestamptz - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestTimestamptzAssignTo(t *testing.T) { - var tim time.Time - var ptim *time.Time - - simpleTests := []struct { - src pgtype.Timestamptz - dst interface{} - expected interface{} - }{ - {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &tim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, - {src: pgtype.Timestamptz{Time: time.Time{}, Status: pgtype.Null}, dst: &ptim, expected: ((*time.Time)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.Timestamptz - dst interface{} - expected interface{} - }{ - {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &ptim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.Timestamptz - dst interface{} - }{ - {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.Infinity, Status: pgtype.Present}, dst: &tim}, - {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}, dst: &tim}, - {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Null}, dst: &tim}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/tsrange.go b/pgtype/tsrange.go deleted file mode 100644 index 54cc863f..00000000 --- a/pgtype/tsrange.go +++ /dev/null @@ -1,250 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Tsrange struct { - Lower Timestamp - Upper Timestamp - LowerType BoundType - UpperType BoundType - Status Status -} - -func (dst *Tsrange) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Tsrange", src) -} - -func (dst *Tsrange) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Tsrange) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Tsrange) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Tsrange{Status: Null} - return nil - } - - utr, err := ParseUntypedTextRange(string(src)) - if err != nil { - return err - } - - *dst = Tsrange{Status: Present} - - dst.LowerType = utr.LowerType - dst.UpperType = utr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeText(ci, []byte(utr.Lower)); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeText(ci, []byte(utr.Upper)); err != nil { - return err - } - } - - return nil -} - -func (dst *Tsrange) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Tsrange{Status: Null} - return nil - } - - ubr, err := ParseUntypedBinaryRange(src) - if err != nil { - return err - } - - *dst = Tsrange{Status: Present} - - dst.LowerType = ubr.LowerType - dst.UpperType = ubr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeBinary(ci, ubr.Lower); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeBinary(ci, ubr.Upper); err != nil { - return err - } - } - - return nil -} - -func (src Tsrange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - switch src.LowerType { - case Exclusive, Unbounded: - buf = append(buf, '(') - case Inclusive: - buf = append(buf, '[') - case Empty: - return append(buf, "empty"...), nil - default: - return nil, errors.Errorf("unknown lower bound type %v", src.LowerType) - } - - var err error - - if src.LowerType != Unbounded { - buf, err = src.Lower.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - } - - buf = append(buf, ',') - - if src.UpperType != Unbounded { - buf, err = src.Upper.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - } - - switch src.UpperType { - case Exclusive, Unbounded: - buf = append(buf, ')') - case Inclusive: - buf = append(buf, ']') - default: - return nil, errors.Errorf("unknown upper bound type %v", src.UpperType) - } - - return buf, nil -} - -func (src Tsrange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var rangeType byte - switch src.LowerType { - case Inclusive: - rangeType |= lowerInclusiveMask - case Unbounded: - rangeType |= lowerUnboundedMask - case Exclusive: - case Empty: - return append(buf, emptyMask), nil - default: - return nil, errors.Errorf("unknown LowerType: %v", src.LowerType) - } - - switch src.UpperType { - case Inclusive: - rangeType |= upperInclusiveMask - case Unbounded: - rangeType |= upperUnboundedMask - case Exclusive: - default: - return nil, errors.Errorf("unknown UpperType: %v", src.UpperType) - } - - buf = append(buf, rangeType) - - var err error - - if src.LowerType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Lower.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - if src.UpperType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Upper.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Tsrange) Scan(src interface{}) error { - if src == nil { - *dst = Tsrange{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src Tsrange) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/tsrange_test.go b/pgtype/tsrange_test.go deleted file mode 100644 index 6215e318..00000000 --- a/pgtype/tsrange_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package pgtype_test - -import ( - "testing" - "time" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestTsrangeTranscode(t *testing.T) { - testutil.TestSuccessfulTranscodeEqFunc(t, "tsrange", []interface{}{ - &pgtype.Tsrange{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Status: pgtype.Present}, - &pgtype.Tsrange{ - Lower: pgtype.Timestamp{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Timestamp{Time: time.Date(2028, 1, 1, 0, 23, 12, 0, time.UTC), Status: pgtype.Present}, - LowerType: pgtype.Inclusive, - UpperType: pgtype.Exclusive, - Status: pgtype.Present, - }, - &pgtype.Tsrange{ - Lower: pgtype.Timestamp{Time: time.Date(1800, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Timestamp{Time: time.Date(2200, 1, 1, 0, 23, 12, 0, time.UTC), Status: pgtype.Present}, - LowerType: pgtype.Inclusive, - UpperType: pgtype.Exclusive, - Status: pgtype.Present, - }, - &pgtype.Tsrange{Status: pgtype.Null}, - }, func(aa, bb interface{}) bool { - a := aa.(pgtype.Tsrange) - b := bb.(pgtype.Tsrange) - - return a.Status == b.Status && - a.Lower.Time.Equal(b.Lower.Time) && - a.Lower.Status == b.Lower.Status && - a.Lower.InfinityModifier == b.Lower.InfinityModifier && - a.Upper.Time.Equal(b.Upper.Time) && - a.Upper.Status == b.Upper.Status && - a.Upper.InfinityModifier == b.Upper.InfinityModifier - }) -} diff --git a/pgtype/tstzrange.go b/pgtype/tstzrange.go deleted file mode 100644 index 1cf2859d..00000000 --- a/pgtype/tstzrange.go +++ /dev/null @@ -1,250 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Tstzrange struct { - Lower Timestamptz - Upper Timestamptz - LowerType BoundType - UpperType BoundType - Status Status -} - -func (dst *Tstzrange) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Tstzrange", src) -} - -func (dst *Tstzrange) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Tstzrange) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Tstzrange) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Tstzrange{Status: Null} - return nil - } - - utr, err := ParseUntypedTextRange(string(src)) - if err != nil { - return err - } - - *dst = Tstzrange{Status: Present} - - dst.LowerType = utr.LowerType - dst.UpperType = utr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeText(ci, []byte(utr.Lower)); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeText(ci, []byte(utr.Upper)); err != nil { - return err - } - } - - return nil -} - -func (dst *Tstzrange) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Tstzrange{Status: Null} - return nil - } - - ubr, err := ParseUntypedBinaryRange(src) - if err != nil { - return err - } - - *dst = Tstzrange{Status: Present} - - dst.LowerType = ubr.LowerType - dst.UpperType = ubr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeBinary(ci, ubr.Lower); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeBinary(ci, ubr.Upper); err != nil { - return err - } - } - - return nil -} - -func (src Tstzrange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - switch src.LowerType { - case Exclusive, Unbounded: - buf = append(buf, '(') - case Inclusive: - buf = append(buf, '[') - case Empty: - return append(buf, "empty"...), nil - default: - return nil, errors.Errorf("unknown lower bound type %v", src.LowerType) - } - - var err error - - if src.LowerType != Unbounded { - buf, err = src.Lower.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - } - - buf = append(buf, ',') - - if src.UpperType != Unbounded { - buf, err = src.Upper.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - } - - switch src.UpperType { - case Exclusive, Unbounded: - buf = append(buf, ')') - case Inclusive: - buf = append(buf, ']') - default: - return nil, errors.Errorf("unknown upper bound type %v", src.UpperType) - } - - return buf, nil -} - -func (src Tstzrange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var rangeType byte - switch src.LowerType { - case Inclusive: - rangeType |= lowerInclusiveMask - case Unbounded: - rangeType |= lowerUnboundedMask - case Exclusive: - case Empty: - return append(buf, emptyMask), nil - default: - return nil, errors.Errorf("unknown LowerType: %v", src.LowerType) - } - - switch src.UpperType { - case Inclusive: - rangeType |= upperInclusiveMask - case Unbounded: - rangeType |= upperUnboundedMask - case Exclusive: - default: - return nil, errors.Errorf("unknown UpperType: %v", src.UpperType) - } - - buf = append(buf, rangeType) - - var err error - - if src.LowerType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Lower.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - if src.UpperType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Upper.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Tstzrange) Scan(src interface{}) error { - if src == nil { - *dst = Tstzrange{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src Tstzrange) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/tstzrange_test.go b/pgtype/tstzrange_test.go deleted file mode 100644 index ddaf798b..00000000 --- a/pgtype/tstzrange_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package pgtype_test - -import ( - "testing" - "time" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestTstzrangeTranscode(t *testing.T) { - testutil.TestSuccessfulTranscodeEqFunc(t, "tstzrange", []interface{}{ - &pgtype.Tstzrange{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Status: pgtype.Present}, - &pgtype.Tstzrange{ - Lower: pgtype.Timestamptz{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Timestamptz{Time: time.Date(2028, 1, 1, 0, 23, 12, 0, time.UTC), Status: pgtype.Present}, - LowerType: pgtype.Inclusive, - UpperType: pgtype.Exclusive, - Status: pgtype.Present, - }, - &pgtype.Tstzrange{ - Lower: pgtype.Timestamptz{Time: time.Date(1800, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Timestamptz{Time: time.Date(2200, 1, 1, 0, 23, 12, 0, time.UTC), Status: pgtype.Present}, - LowerType: pgtype.Inclusive, - UpperType: pgtype.Exclusive, - Status: pgtype.Present, - }, - &pgtype.Tstzrange{Status: pgtype.Null}, - }, func(aa, bb interface{}) bool { - a := aa.(pgtype.Tstzrange) - b := bb.(pgtype.Tstzrange) - - return a.Status == b.Status && - a.Lower.Time.Equal(b.Lower.Time) && - a.Lower.Status == b.Lower.Status && - a.Lower.InfinityModifier == b.Lower.InfinityModifier && - a.Upper.Time.Equal(b.Upper.Time) && - a.Upper.Status == b.Upper.Status && - a.Upper.InfinityModifier == b.Upper.InfinityModifier - }) -} diff --git a/pgtype/typed_array.go.erb b/pgtype/typed_array.go.erb deleted file mode 100644 index 3ee637aa..00000000 --- a/pgtype/typed_array.go.erb +++ /dev/null @@ -1,305 +0,0 @@ -package pgtype - -import ( - "bytes" - "fmt" - "io" - - "github.com/jackc/pgio" -) - -type <%= pgtype_array_type %> struct { - Elements []<%= pgtype_element_type %> - Dimensions []ArrayDimension - Status Status -} - -func (dst *<%= pgtype_array_type %>) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = <%= pgtype_array_type %>{Status: Null} - return nil - } - - switch value := src.(type) { - <% go_array_types.split(",").each do |t| %> - case <%= t %>: - if value == nil { - *dst = <%= pgtype_array_type %>{Status: Null} - } else if len(value) == 0 { - *dst = <%= pgtype_array_type %>{Status: Present} - } else { - elements := make([]<%= pgtype_element_type %>, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = <%= pgtype_array_type %>{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - <% end %> - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to <%= pgtype_array_type %>", value) - } - - return nil -} - -func (dst *<%= pgtype_array_type %>) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *<%= pgtype_array_type %>) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - <% go_array_types.split(",").each do |t| %> - case *<%= t %>: - *v = make(<%= t %>, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - <% end %> - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *<%= pgtype_array_type %>) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = <%= pgtype_array_type %>{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []<%= pgtype_element_type %> - - if len(uta.Elements) > 0 { - elements = make([]<%= pgtype_element_type %>, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem <%= pgtype_element_type %> - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = <%= pgtype_array_type %>{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -<% if binary_format == "true" %> -func (dst *<%= pgtype_array_type %>) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = <%= pgtype_array_type %>{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = <%= pgtype_array_type %>{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]<%= pgtype_element_type %>, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp:rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = <%= pgtype_array_type %>{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} -<% end %> - -func (src *<%= pgtype_array_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `<%= text_null %>`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -<% if binary_format == "true" %> - func (src *<%= pgtype_array_type %>) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("<%= element_type_name %>"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "<%= element_type_name %>") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil - } -<% end %> - -// Scan implements the database/sql Scanner interface. -func (dst *<%= pgtype_array_type %>) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *<%= pgtype_array_type %>) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/typed_array_gen.sh b/pgtype/typed_array_gen.sh deleted file mode 100644 index 911fa392..00000000 --- a/pgtype/typed_array_gen.sh +++ /dev/null @@ -1,25 +0,0 @@ -erb pgtype_array_type=Int2Array pgtype_element_type=Int2 go_array_types=[]int16,[]uint16 element_type_name=int2 text_null=NULL binary_format=true typed_array.go.erb > int2_array.go -erb pgtype_array_type=Int4Array pgtype_element_type=Int4 go_array_types=[]int32,[]uint32 element_type_name=int4 text_null=NULL binary_format=true typed_array.go.erb > int4_array.go -erb pgtype_array_type=Int8Array pgtype_element_type=Int8 go_array_types=[]int64,[]uint64 element_type_name=int8 text_null=NULL binary_format=true typed_array.go.erb > int8_array.go -erb pgtype_array_type=BoolArray pgtype_element_type=Bool go_array_types=[]bool element_type_name=bool text_null=NULL binary_format=true typed_array.go.erb > bool_array.go -erb pgtype_array_type=DateArray pgtype_element_type=Date go_array_types=[]time.Time element_type_name=date text_null=NULL binary_format=true typed_array.go.erb > date_array.go -erb pgtype_array_type=TimestamptzArray pgtype_element_type=Timestamptz go_array_types=[]time.Time element_type_name=timestamptz text_null=NULL binary_format=true typed_array.go.erb > timestamptz_array.go -erb pgtype_array_type=TimestampArray pgtype_element_type=Timestamp go_array_types=[]time.Time element_type_name=timestamp text_null=NULL binary_format=true typed_array.go.erb > timestamp_array.go -erb pgtype_array_type=Float4Array pgtype_element_type=Float4 go_array_types=[]float32 element_type_name=float4 text_null=NULL binary_format=true typed_array.go.erb > float4_array.go -erb pgtype_array_type=Float8Array pgtype_element_type=Float8 go_array_types=[]float64 element_type_name=float8 text_null=NULL binary_format=true typed_array.go.erb > float8_array.go -erb pgtype_array_type=InetArray pgtype_element_type=Inet go_array_types=[]*net.IPNet,[]net.IP element_type_name=inet text_null=NULL binary_format=true typed_array.go.erb > inet_array.go -erb pgtype_array_type=MacaddrArray pgtype_element_type=Macaddr go_array_types=[]net.HardwareAddr element_type_name=macaddr text_null=NULL binary_format=true typed_array.go.erb > macaddr_array.go -erb pgtype_array_type=CIDRArray pgtype_element_type=CIDR go_array_types=[]*net.IPNet,[]net.IP element_type_name=cidr text_null=NULL binary_format=true typed_array.go.erb > cidr_array.go -erb pgtype_array_type=TextArray pgtype_element_type=Text go_array_types=[]string element_type_name=text text_null=NULL binary_format=true typed_array.go.erb > text_array.go -erb pgtype_array_type=VarcharArray pgtype_element_type=Varchar go_array_types=[]string element_type_name=varchar text_null=NULL binary_format=true typed_array.go.erb > varchar_array.go -erb pgtype_array_type=BPCharArray pgtype_element_type=BPChar go_array_types=[]string element_type_name=bpchar text_null=NULL binary_format=true typed_array.go.erb > bpchar_array.go -erb pgtype_array_type=ByteaArray pgtype_element_type=Bytea go_array_types=[][]byte element_type_name=bytea text_null=NULL binary_format=true typed_array.go.erb > bytea_array.go -erb pgtype_array_type=ACLItemArray pgtype_element_type=ACLItem go_array_types=[]string element_type_name=aclitem text_null=NULL binary_format=false typed_array.go.erb > aclitem_array.go -erb pgtype_array_type=HstoreArray pgtype_element_type=Hstore go_array_types=[]map[string]string element_type_name=hstore text_null=NULL binary_format=true typed_array.go.erb > hstore_array.go -erb pgtype_array_type=NumericArray pgtype_element_type=Numeric go_array_types=[]float32,[]float64,[]int64,[]uint64 element_type_name=numeric text_null=NULL binary_format=true typed_array.go.erb > numeric_array.go -erb pgtype_array_type=UUIDArray pgtype_element_type=UUID go_array_types=[][16]byte,[][]byte,[]string element_type_name=uuid text_null=NULL binary_format=true typed_array.go.erb > uuid_array.go - -# While the binary format is theoretically possible it is only practical to use the text format. -erb pgtype_array_type=EnumArray pgtype_element_type=GenericText go_array_types=[]string text_null=NULL binary_format=false typed_array.go.erb > enum_array.go - -goimports -w *_array.go diff --git a/pgtype/typed_range.go.erb b/pgtype/typed_range.go.erb deleted file mode 100644 index 035a71af..00000000 --- a/pgtype/typed_range.go.erb +++ /dev/null @@ -1,252 +0,0 @@ -package pgtype - -import ( - "bytes" - "database/sql/driver" - "fmt" - "io" - - "github.com/jackc/pgio" -) - -type <%= range_type %> struct { - Lower <%= element_type %> - Upper <%= element_type %> - LowerType BoundType - UpperType BoundType - Status Status -} - -func (dst *<%= range_type %>) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to <%= range_type %>", src) -} - -func (dst *<%= range_type %>) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *<%= range_type %>) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *<%= range_type %>) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = <%= range_type %>{Status: Null} - return nil - } - - utr, err := ParseUntypedTextRange(string(src)) - if err != nil { - return err - } - - *dst = <%= range_type %>{Status: Present} - - dst.LowerType = utr.LowerType - dst.UpperType = utr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeText(ci, []byte(utr.Lower)); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeText(ci, []byte(utr.Upper)); err != nil { - return err - } - } - - return nil -} - -func (dst *<%= range_type %>) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = <%= range_type %>{Status: Null} - return nil - } - - ubr, err := ParseUntypedBinaryRange(src) - if err != nil { - return err - } - - *dst = <%= range_type %>{Status: Present} - - dst.LowerType = ubr.LowerType - dst.UpperType = ubr.UpperType - - if dst.LowerType == Empty { - return nil - } - - if dst.LowerType == Inclusive || dst.LowerType == Exclusive { - if err := dst.Lower.DecodeBinary(ci, ubr.Lower); err != nil { - return err - } - } - - if dst.UpperType == Inclusive || dst.UpperType == Exclusive { - if err := dst.Upper.DecodeBinary(ci, ubr.Upper); err != nil { - return err - } - } - - return nil -} - -func (src <%= range_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - switch src.LowerType { - case Exclusive, Unbounded: - buf = append(buf, '(') - case Inclusive: - buf = append(buf, '[') - case Empty: - return append(buf, "empty"...), nil - default: - return nil, errors.Errorf("unknown lower bound type %v", src.LowerType) - } - - var err error - - if src.LowerType != Unbounded { - buf, err = src.Lower.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - } - - buf = append(buf, ',') - - if src.UpperType != Unbounded { - buf, err = src.Upper.EncodeText(ci, buf) - if err != nil { - return nil, err - } else if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - } - - switch src.UpperType { - case Exclusive, Unbounded: - buf = append(buf, ')') - case Inclusive: - buf = append(buf, ']') - default: - return nil, errors.Errorf("unknown upper bound type %v", src.UpperType) - } - - return buf, nil -} - -func (src <%= range_type %>) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - var rangeType byte - switch src.LowerType { - case Inclusive: - rangeType |= lowerInclusiveMask - case Unbounded: - rangeType |= lowerUnboundedMask - case Exclusive: - case Empty: - return append(buf, emptyMask), nil - default: - return nil, errors.Errorf("unknown LowerType: %v", src.LowerType) - } - - switch src.UpperType { - case Inclusive: - rangeType |= upperInclusiveMask - case Unbounded: - rangeType |= upperUnboundedMask - case Exclusive: - default: - return nil, errors.Errorf("unknown UpperType: %v", src.UpperType) - } - - buf = append(buf, rangeType) - - var err error - - if src.LowerType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Lower.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - if src.UpperType != Unbounded { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - buf, err = src.Upper.EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if buf == nil { - return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded") - } - - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *<%= range_type %>) Scan(src interface{}) error { - if src == nil { - *dst = <%= range_type %>{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src <%= range_type %>) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/typed_range_gen.sh b/pgtype/typed_range_gen.sh deleted file mode 100644 index bedda292..00000000 --- a/pgtype/typed_range_gen.sh +++ /dev/null @@ -1,7 +0,0 @@ -erb range_type=Int4range element_type=Int4 typed_range.go.erb > int4range.go -erb range_type=Int8range element_type=Int8 typed_range.go.erb > int8range.go -erb range_type=Tsrange element_type=Timestamp typed_range.go.erb > tsrange.go -erb range_type=Tstzrange element_type=Timestamptz typed_range.go.erb > tstzrange.go -erb range_type=Daterange element_type=Date typed_range.go.erb > daterange.go -erb range_type=Numrange element_type=Numeric typed_range.go.erb > numrange.go -goimports -w *range.go diff --git a/pgtype/unknown.go b/pgtype/unknown.go deleted file mode 100644 index 567831d7..00000000 --- a/pgtype/unknown.go +++ /dev/null @@ -1,44 +0,0 @@ -package pgtype - -import "database/sql/driver" - -// Unknown represents the PostgreSQL unknown type. It is either a string literal -// or NULL. It is used when PostgreSQL does not know the type of a value. In -// general, this will only be used in pgx when selecting a null value without -// type information. e.g. SELECT NULL; -type Unknown struct { - String string - Status Status -} - -func (dst *Unknown) Set(src interface{}) error { - return (*Text)(dst).Set(src) -} - -func (dst *Unknown) Get() interface{} { - return (*Text)(dst).Get() -} - -// AssignTo assigns from src to dst. Note that as Unknown is not a general number -// type AssignTo does not do automatic type conversion as other number types do. -func (src *Unknown) AssignTo(dst interface{}) error { - return (*Text)(src).AssignTo(dst) -} - -func (dst *Unknown) DecodeText(ci *ConnInfo, src []byte) error { - return (*Text)(dst).DecodeText(ci, src) -} - -func (dst *Unknown) DecodeBinary(ci *ConnInfo, src []byte) error { - return (*Text)(dst).DecodeBinary(ci, src) -} - -// Scan implements the database/sql Scanner interface. -func (dst *Unknown) Scan(src interface{}) error { - return (*Text)(dst).Scan(src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Unknown) Value() (driver.Value, error) { - return (*Text)(src).Value() -} diff --git a/pgtype/uuid.go b/pgtype/uuid.go deleted file mode 100644 index d3e68f5c..00000000 --- a/pgtype/uuid.go +++ /dev/null @@ -1,186 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/hex" - "fmt" - - errors "golang.org/x/xerrors" -) - -type UUID struct { - Bytes [16]byte - Status Status -} - -func (dst *UUID) Set(src interface{}) error { - if src == nil { - *dst = UUID{Status: Null} - return nil - } - - switch value := src.(type) { - case [16]byte: - *dst = UUID{Bytes: value, Status: Present} - case []byte: - if value != nil { - if len(value) != 16 { - return errors.Errorf("[]byte must be 16 bytes to convert to UUID: %d", len(value)) - } - *dst = UUID{Status: Present} - copy(dst.Bytes[:], value) - } else { - *dst = UUID{Status: Null} - } - case string: - uuid, err := parseUUID(value) - if err != nil { - return err - } - *dst = UUID{Bytes: uuid, Status: Present} - default: - if originalSrc, ok := underlyingPtrType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to UUID", value) - } - - return nil -} - -func (dst *UUID) Get() interface{} { - switch dst.Status { - case Present: - return dst.Bytes - case Null: - return nil - default: - return dst.Status - } -} - -func (src *UUID) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *[16]byte: - *v = src.Bytes - return nil - case *[]byte: - *v = make([]byte, 16) - copy(*v, src.Bytes[:]) - return nil - case *string: - *v = encodeUUID(src.Bytes) - return nil - default: - if nextDst, retry := GetAssignToDstType(v); retry { - return src.AssignTo(nextDst) - } - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot assign %v into %T", src, dst) -} - -// parseUUID converts a string UUID in standard form to a byte array. -func parseUUID(src string) (dst [16]byte, err error) { - if len(src) < 36 { - return dst, errors.Errorf("cannot parse UUID %v", src) - } - src = src[0:8] + src[9:13] + src[14:18] + src[19:23] + src[24:] - buf, err := hex.DecodeString(src) - if err != nil { - return dst, err - } - - copy(dst[:], buf) - return dst, err -} - -// encodeUUID converts a uuid byte array to UUID standard string form. -func encodeUUID(src [16]byte) string { - return fmt.Sprintf("%x-%x-%x-%x-%x", src[0:4], src[4:6], src[6:8], src[8:10], src[10:16]) -} - -func (dst *UUID) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = UUID{Status: Null} - return nil - } - - if len(src) != 36 { - return errors.Errorf("invalid length for UUID: %v", len(src)) - } - - buf, err := parseUUID(string(src)) - if err != nil { - return err - } - - *dst = UUID{Bytes: buf, Status: Present} - return nil -} - -func (dst *UUID) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = UUID{Status: Null} - return nil - } - - if len(src) != 16 { - return errors.Errorf("invalid length for UUID: %v", len(src)) - } - - *dst = UUID{Status: Present} - copy(dst.Bytes[:], src) - return nil -} - -func (src *UUID) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, encodeUUID(src.Bytes)...), nil -} - -func (src *UUID) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - return append(buf, src.Bytes[:]...), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *UUID) Scan(src interface{}) error { - if src == nil { - *dst = UUID{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *UUID) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/uuid_array.go b/pgtype/uuid_array.go deleted file mode 100644 index 1d28ee59..00000000 --- a/pgtype/uuid_array.go +++ /dev/null @@ -1,357 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type UUIDArray struct { - Elements []UUID - Dimensions []ArrayDimension - Status Status -} - -func (dst *UUIDArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = UUIDArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case [][16]byte: - if value == nil { - *dst = UUIDArray{Status: Null} - } else if len(value) == 0 { - *dst = UUIDArray{Status: Present} - } else { - elements := make([]UUID, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = UUIDArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - case [][]byte: - if value == nil { - *dst = UUIDArray{Status: Null} - } else if len(value) == 0 { - *dst = UUIDArray{Status: Present} - } else { - elements := make([]UUID, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = UUIDArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - case []string: - if value == nil { - *dst = UUIDArray{Status: Null} - } else if len(value) == 0 { - *dst = UUIDArray{Status: Present} - } else { - elements := make([]UUID, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = UUIDArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to UUIDArray", value) - } - - return nil -} - -func (dst *UUIDArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *UUIDArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[][16]byte: - *v = make([][16]byte, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[][]byte: - *v = make([][]byte, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]string: - *v = make([]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *UUIDArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = UUIDArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []UUID - - if len(uta.Elements) > 0 { - elements = make([]UUID, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem UUID - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = UUIDArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *UUIDArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = UUIDArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = UUIDArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]UUID, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = UUIDArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *UUIDArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *UUIDArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("uuid"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "uuid") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *UUIDArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *UUIDArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/uuid_array_test.go b/pgtype/uuid_array_test.go deleted file mode 100644 index 6ec6acfb..00000000 --- a/pgtype/uuid_array_test.go +++ /dev/null @@ -1,205 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestUUIDArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "uuid[]", []interface{}{ - &pgtype.UUIDArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.UUIDArray{ - Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.UUIDArray{Status: pgtype.Null}, - &pgtype.UUIDArray{ - Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}, - {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Status: pgtype.Present}, - {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Bytes: [16]byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.UUIDArray{ - Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}, - {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Status: pgtype.Present}, - {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestUUIDArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.UUIDArray - }{ - { - source: nil, - result: pgtype.UUIDArray{Status: pgtype.Null}, - }, - { - source: [][16]byte{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}, - result: pgtype.UUIDArray{ - Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: [][16]byte{}, - result: pgtype.UUIDArray{Status: pgtype.Present}, - }, - { - source: ([][16]byte)(nil), - result: pgtype.UUIDArray{Status: pgtype.Null}, - }, - { - source: [][]byte{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}, - result: pgtype.UUIDArray{ - Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: [][]byte{ - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, - nil, - {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, - }, - result: pgtype.UUIDArray{ - Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 4}}, - Status: pgtype.Present}, - }, - { - source: [][]byte{}, - result: pgtype.UUIDArray{Status: pgtype.Present}, - }, - { - source: ([][]byte)(nil), - result: pgtype.UUIDArray{Status: pgtype.Null}, - }, - { - source: []string{"00010203-0405-0607-0809-0a0b0c0d0e0f"}, - result: pgtype.UUIDArray{ - Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: []string{}, - result: pgtype.UUIDArray{Status: pgtype.Present}, - }, - { - source: ([]string)(nil), - result: pgtype.UUIDArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.UUIDArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestUUIDArrayAssignTo(t *testing.T) { - var byteArraySlice [][16]byte - var byteSliceSlice [][]byte - var stringSlice []string - - simpleTests := []struct { - src pgtype.UUIDArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.UUIDArray{ - Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &byteArraySlice, - expected: [][16]byte{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}, - }, - { - src: pgtype.UUIDArray{Status: pgtype.Null}, - dst: &byteArraySlice, - expected: ([][16]byte)(nil), - }, - { - src: pgtype.UUIDArray{ - Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &byteSliceSlice, - expected: [][]byte{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}, - }, - { - src: pgtype.UUIDArray{Status: pgtype.Null}, - dst: &byteSliceSlice, - expected: ([][]byte)(nil), - }, - { - src: pgtype.UUIDArray{ - Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &stringSlice, - expected: []string{"00010203-0405-0607-0809-0a0b0c0d0e0f"}, - }, - { - src: pgtype.UUIDArray{Status: pgtype.Null}, - dst: &stringSlice, - expected: ([]string)(nil), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } -} diff --git a/pgtype/uuid_test.go b/pgtype/uuid_test.go deleted file mode 100644 index 9d95c10c..00000000 --- a/pgtype/uuid_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package pgtype_test - -import ( - "bytes" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestUUIDTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "uuid", []interface{}{ - &pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - &pgtype.UUID{Status: pgtype.Null}, - }) -} - -func TestUUIDSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.UUID - }{ - { - source: nil, - result: pgtype.UUID{Status: pgtype.Null}, - }, - { - source: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - }, - { - source: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - }, - { - source: ([]byte)(nil), - result: pgtype.UUID{Status: pgtype.Null}, - }, - { - source: "00010203-0405-0607-0809-0a0b0c0d0e0f", - result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.UUID - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestUUIDAssignTo(t *testing.T) { - { - src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} - var dst [16]byte - expected := [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} - - err := src.AssignTo(&dst) - if err != nil { - t.Error(err) - } - - if dst != expected { - t.Errorf("expected %v to assign %v, but result was %v", src, expected, dst) - } - } - - { - src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} - var dst []byte - expected := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} - - err := src.AssignTo(&dst) - if err != nil { - t.Error(err) - } - - if bytes.Compare(dst, expected) != 0 { - t.Errorf("expected %v to assign %v, but result was %v", src, expected, dst) - } - } - - { - src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} - var dst string - expected := "00010203-0405-0607-0809-0a0b0c0d0e0f" - - err := src.AssignTo(&dst) - if err != nil { - t.Error(err) - } - - if dst != expected { - t.Errorf("expected %v to assign %v, but result was %v", src, expected, dst) - } - } - -} diff --git a/pgtype/varbit.go b/pgtype/varbit.go deleted file mode 100644 index fe4db33d..00000000 --- a/pgtype/varbit.go +++ /dev/null @@ -1,133 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type Varbit struct { - Bytes []byte - Len int32 // Number of bits - Status Status -} - -func (dst *Varbit) Set(src interface{}) error { - return errors.Errorf("cannot convert %v to Varbit", src) -} - -func (dst *Varbit) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *Varbit) AssignTo(dst interface{}) error { - return errors.Errorf("cannot assign %v to %T", src, dst) -} - -func (dst *Varbit) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Varbit{Status: Null} - return nil - } - - bitLen := len(src) - byteLen := bitLen / 8 - if bitLen%8 > 0 { - byteLen++ - } - buf := make([]byte, byteLen) - - for i, b := range src { - if b == '1' { - byteIdx := i / 8 - bitIdx := uint(i % 8) - buf[byteIdx] = buf[byteIdx] | (128 >> bitIdx) - } - } - - *dst = Varbit{Bytes: buf, Len: int32(bitLen), Status: Present} - return nil -} - -func (dst *Varbit) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = Varbit{Status: Null} - return nil - } - - if len(src) < 4 { - return errors.Errorf("invalid length for varbit: %v", len(src)) - } - - bitLen := int32(binary.BigEndian.Uint32(src)) - rp := 4 - - *dst = Varbit{Bytes: src[rp:], Len: bitLen, Status: Present} - return nil -} - -func (src *Varbit) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - for i := int32(0); i < src.Len; i++ { - byteIdx := i / 8 - bitMask := byte(128 >> byte(i%8)) - char := byte('0') - if src.Bytes[byteIdx]&bitMask > 0 { - char = '1' - } - buf = append(buf, char) - } - - return buf, nil -} - -func (src *Varbit) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - buf = pgio.AppendInt32(buf, src.Len) - return append(buf, src.Bytes...), nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *Varbit) Scan(src interface{}) error { - if src == nil { - *dst = Varbit{Status: Null} - return nil - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Varbit) Value() (driver.Value, error) { - return EncodeValueText(src) -} diff --git a/pgtype/varbit_test.go b/pgtype/varbit_test.go deleted file mode 100644 index 8ea282eb..00000000 --- a/pgtype/varbit_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package pgtype_test - -import ( - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestVarbitTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "varbit", []interface{}{ - &pgtype.Varbit{Bytes: []byte{}, Len: 0, Status: pgtype.Present}, - &pgtype.Varbit{Bytes: []byte{0, 1, 128, 254, 255}, Len: 40, Status: pgtype.Present}, - &pgtype.Varbit{Bytes: []byte{0, 1, 128, 254, 128}, Len: 33, Status: pgtype.Present}, - &pgtype.Varbit{Status: pgtype.Null}, - }) -} - -func TestVarbitNormalize(t *testing.T) { - testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{ - { - SQL: "select B'111111111'", - Value: &pgtype.Varbit{Bytes: []byte{255, 128}, Len: 9, Status: pgtype.Present}, - }, - }) -} diff --git a/pgtype/varchar.go b/pgtype/varchar.go deleted file mode 100644 index 6be1a035..00000000 --- a/pgtype/varchar.go +++ /dev/null @@ -1,58 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" -) - -type Varchar Text - -// Set converts from src to dst. Note that as Varchar is not a general -// number type Set does not do automatic type conversion as other number -// types do. -func (dst *Varchar) Set(src interface{}) error { - return (*Text)(dst).Set(src) -} - -func (dst *Varchar) Get() interface{} { - return (*Text)(dst).Get() -} - -// AssignTo assigns from src to dst. Note that as Varchar is not a general number -// type AssignTo does not do automatic type conversion as other number types do. -func (src *Varchar) AssignTo(dst interface{}) error { - return (*Text)(src).AssignTo(dst) -} - -func (dst *Varchar) DecodeText(ci *ConnInfo, src []byte) error { - return (*Text)(dst).DecodeText(ci, src) -} - -func (dst *Varchar) DecodeBinary(ci *ConnInfo, src []byte) error { - return (*Text)(dst).DecodeBinary(ci, src) -} - -func (src *Varchar) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*Text)(src).EncodeText(ci, buf) -} - -func (src *Varchar) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*Text)(src).EncodeBinary(ci, buf) -} - -// Scan implements the database/sql Scanner interface. -func (dst *Varchar) Scan(src interface{}) error { - return (*Text)(dst).Scan(src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *Varchar) Value() (driver.Value, error) { - return (*Text)(src).Value() -} - -func (src *Varchar) MarshalJSON() ([]byte, error) { - return (*Text)(src).MarshalJSON() -} - -func (dst *Varchar) UnmarshalJSON(b []byte) error { - return (*Text)(dst).UnmarshalJSON(b) -} diff --git a/pgtype/varchar_array.go b/pgtype/varchar_array.go deleted file mode 100644 index 6aa92337..00000000 --- a/pgtype/varchar_array.go +++ /dev/null @@ -1,301 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" - "encoding/binary" - - "github.com/jackc/pgio" - errors "golang.org/x/xerrors" -) - -type VarcharArray struct { - Elements []Varchar - Dimensions []ArrayDimension - Status Status -} - -func (dst *VarcharArray) Set(src interface{}) error { - // untyped nil and typed nil interfaces are different - if src == nil { - *dst = VarcharArray{Status: Null} - return nil - } - - switch value := src.(type) { - - case []string: - if value == nil { - *dst = VarcharArray{Status: Null} - } else if len(value) == 0 { - *dst = VarcharArray{Status: Present} - } else { - elements := make([]Varchar, len(value)) - for i := range value { - if err := elements[i].Set(value[i]); err != nil { - return err - } - } - *dst = VarcharArray{ - Elements: elements, - Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, - } - } - - default: - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to VarcharArray", value) - } - - return nil -} - -func (dst *VarcharArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } -} - -func (src *VarcharArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - - case *[]string: - *v = make([]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return errors.Errorf("unable to assign to %T", dst) - } - case Null: - return NullAssignTo(dst) - } - - return errors.Errorf("cannot decode %#v into %T", src, dst) -} - -func (dst *VarcharArray) DecodeText(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = VarcharArray{Status: Null} - return nil - } - - uta, err := ParseUntypedTextArray(string(src)) - if err != nil { - return err - } - - var elements []Varchar - - if len(uta.Elements) > 0 { - elements = make([]Varchar, len(uta.Elements)) - - for i, s := range uta.Elements { - var elem Varchar - var elemSrc []byte - if s != "NULL" { - elemSrc = []byte(s) - } - err = elem.DecodeText(ci, elemSrc) - if err != nil { - return err - } - - elements[i] = elem - } - } - - *dst = VarcharArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} - - return nil -} - -func (dst *VarcharArray) DecodeBinary(ci *ConnInfo, src []byte) error { - if src == nil { - *dst = VarcharArray{Status: Null} - return nil - } - - var arrayHeader ArrayHeader - rp, err := arrayHeader.DecodeBinary(ci, src) - if err != nil { - return err - } - - if len(arrayHeader.Dimensions) == 0 { - *dst = VarcharArray{Dimensions: arrayHeader.Dimensions, Status: Present} - return nil - } - - elementCount := arrayHeader.Dimensions[0].Length - for _, d := range arrayHeader.Dimensions[1:] { - elementCount *= d.Length - } - - elements := make([]Varchar, elementCount) - - for i := range elements { - elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) - rp += 4 - var elemSrc []byte - if elemLen >= 0 { - elemSrc = src[rp : rp+elemLen] - rp += elemLen - } - err = elements[i].DecodeBinary(ci, elemSrc) - if err != nil { - return err - } - } - - *dst = VarcharArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} - return nil -} - -func (src *VarcharArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - if len(src.Dimensions) == 0 { - return append(buf, '{', '}'), nil - } - - buf = EncodeTextArrayDimensions(buf, src.Dimensions) - - // dimElemCounts is the multiples of elements that each array lies on. For - // example, a single dimension array of length 4 would have a dimElemCounts of - // [4]. A multi-dimensional array of lengths [3,5,2] would have a - // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' - // or '}'. - dimElemCounts := make([]int, len(src.Dimensions)) - dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) - for i := len(src.Dimensions) - 2; i > -1; i-- { - dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] - } - - inElemBuf := make([]byte, 0, 32) - for i, elem := range src.Elements { - if i > 0 { - buf = append(buf, ',') - } - - for _, dec := range dimElemCounts { - if i%dec == 0 { - buf = append(buf, '{') - } - } - - elemBuf, err := elem.EncodeText(ci, inElemBuf) - if err != nil { - return nil, err - } - if elemBuf == nil { - buf = append(buf, `NULL`...) - } else { - buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) - } - - for _, dec := range dimElemCounts { - if (i+1)%dec == 0 { - buf = append(buf, '}') - } - } - } - - return buf, nil -} - -func (src *VarcharArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: - return nil, nil - case Undefined: - return nil, errUndefined - } - - arrayHeader := ArrayHeader{ - Dimensions: src.Dimensions, - } - - if dt, ok := ci.DataTypeForName("varchar"); ok { - arrayHeader.ElementOID = int32(dt.OID) - } else { - return nil, errors.Errorf("unable to find oid for type name %v", "varchar") - } - - for i := range src.Elements { - if src.Elements[i].Status == Null { - arrayHeader.ContainsNull = true - break - } - } - - buf = arrayHeader.EncodeBinary(ci, buf) - - for i := range src.Elements { - sp := len(buf) - buf = pgio.AppendInt32(buf, -1) - - elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) - if err != nil { - return nil, err - } - if elemBuf != nil { - buf = elemBuf - pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) - } - } - - return buf, nil -} - -// Scan implements the database/sql Scanner interface. -func (dst *VarcharArray) Scan(src interface{}) error { - if src == nil { - return dst.DecodeText(nil, nil) - } - - switch src := src.(type) { - case string: - return dst.DecodeText(nil, []byte(src)) - case []byte: - srcCopy := make([]byte, len(src)) - copy(srcCopy, src) - return dst.DecodeText(nil, srcCopy) - } - - return errors.Errorf("cannot scan %T", src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *VarcharArray) Value() (driver.Value, error) { - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - if buf == nil { - return nil, nil - } - - return string(buf), nil -} diff --git a/pgtype/varchar_array_test.go b/pgtype/varchar_array_test.go deleted file mode 100644 index b836664f..00000000 --- a/pgtype/varchar_array_test.go +++ /dev/null @@ -1,152 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestVarcharArrayTranscode(t *testing.T) { - testutil.TestSuccessfulTranscode(t, "varchar[]", []interface{}{ - &pgtype.VarcharArray{ - Elements: nil, - Dimensions: nil, - Status: pgtype.Present, - }, - &pgtype.VarcharArray{ - Elements: []pgtype.Varchar{ - {String: "foo", Status: pgtype.Present}, - {Status: pgtype.Null}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.VarcharArray{Status: pgtype.Null}, - &pgtype.VarcharArray{ - Elements: []pgtype.Varchar{ - {String: "bar ", Status: pgtype.Present}, - {String: "NuLL", Status: pgtype.Present}, - {String: `wow"quz\`, Status: pgtype.Present}, - {String: "", Status: pgtype.Present}, - {Status: pgtype.Null}, - {String: "null", Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, - }, - &pgtype.VarcharArray{ - Elements: []pgtype.Varchar{ - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "quz", Status: pgtype.Present}, - {String: "foo", Status: pgtype.Present}, - }, - Dimensions: []pgtype.ArrayDimension{ - {Length: 2, LowerBound: 4}, - {Length: 2, LowerBound: 2}, - }, - Status: pgtype.Present, - }, - }) -} - -func TestVarcharArraySet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.VarcharArray - }{ - { - source: []string{"foo"}, - result: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{String: "foo", Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, - }, - { - source: (([]string)(nil)), - result: pgtype.VarcharArray{Status: pgtype.Null}, - }, - } - - for i, tt := range successfulTests { - var r pgtype.VarcharArray - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if !reflect.DeepEqual(r, tt.result) { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestVarcharArrayAssignTo(t *testing.T) { - var stringSlice []string - type _stringSlice []string - var namedStringSlice _stringSlice - - simpleTests := []struct { - src pgtype.VarcharArray - dst interface{} - expected interface{} - }{ - { - src: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{String: "foo", Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &stringSlice, - expected: []string{"foo"}, - }, - { - src: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{String: "bar", Status: pgtype.Present}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &namedStringSlice, - expected: _stringSlice{"bar"}, - }, - { - src: pgtype.VarcharArray{Status: pgtype.Null}, - dst: &stringSlice, - expected: (([]string)(nil)), - }, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.VarcharArray - dst interface{} - }{ - { - src: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{Status: pgtype.Null}}, - Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, - }, - dst: &stringSlice, - }, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/pgtype/xid.go b/pgtype/xid.go deleted file mode 100644 index f66f5367..00000000 --- a/pgtype/xid.go +++ /dev/null @@ -1,64 +0,0 @@ -package pgtype - -import ( - "database/sql/driver" -) - -// XID is PostgreSQL's Transaction ID type. -// -// In later versions of PostgreSQL, it is the type used for the backend_xid -// and backend_xmin columns of the pg_stat_activity system view. -// -// Also, when one does -// -// select xmin, xmax, * from some_table; -// -// it is the data type of the xmin and xmax hidden system columns. -// -// It is currently implemented as an unsigned four byte integer. -// Its definition can be found in src/include/postgres_ext.h as TransactionId -// in the PostgreSQL sources. -type XID pguint32 - -// Set converts from src to dst. Note that as XID is not a general -// number type Set does not do automatic type conversion as other number -// types do. -func (dst *XID) Set(src interface{}) error { - return (*pguint32)(dst).Set(src) -} - -func (dst *XID) Get() interface{} { - return (*pguint32)(dst).Get() -} - -// AssignTo assigns from src to dst. Note that as XID is not a general number -// type AssignTo does not do automatic type conversion as other number types do. -func (src *XID) AssignTo(dst interface{}) error { - return (*pguint32)(src).AssignTo(dst) -} - -func (dst *XID) DecodeText(ci *ConnInfo, src []byte) error { - return (*pguint32)(dst).DecodeText(ci, src) -} - -func (dst *XID) DecodeBinary(ci *ConnInfo, src []byte) error { - return (*pguint32)(dst).DecodeBinary(ci, src) -} - -func (src *XID) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*pguint32)(src).EncodeText(ci, buf) -} - -func (src *XID) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - return (*pguint32)(src).EncodeBinary(ci, buf) -} - -// Scan implements the database/sql Scanner interface. -func (dst *XID) Scan(src interface{}) error { - return (*pguint32)(dst).Scan(src) -} - -// Value implements the database/sql/driver Valuer interface. -func (src *XID) Value() (driver.Value, error) { - return (*pguint32)(src).Value() -} diff --git a/pgtype/xid_test.go b/pgtype/xid_test.go deleted file mode 100644 index 34801e1f..00000000 --- a/pgtype/xid_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package pgtype_test - -import ( - "reflect" - "testing" - - "github.com/jackc/pgx/v4/pgtype" - "github.com/jackc/pgx/v4/pgtype/testutil" -) - -func TestXIDTranscode(t *testing.T) { - pgTypeName := "xid" - values := []interface{}{ - &pgtype.XID{Uint: 42, Status: pgtype.Present}, - &pgtype.XID{Status: pgtype.Null}, - } - eqFunc := func(a, b interface{}) bool { - return reflect.DeepEqual(a, b) - } - - testutil.TestPgxSuccessfulTranscodeEqFunc(t, pgTypeName, values, eqFunc) - - for _, driverName := range []string{"github.com/lib/pq", "github.com/jackc/pgx/stdlib"} { - testutil.TestDatabaseSQLSuccessfulTranscodeEqFunc(t, driverName, pgTypeName, values, eqFunc) - } -} - -func TestXIDSet(t *testing.T) { - successfulTests := []struct { - source interface{} - result pgtype.XID - }{ - {source: uint32(1), result: pgtype.XID{Uint: 1, Status: pgtype.Present}}, - } - - for i, tt := range successfulTests { - var r pgtype.XID - err := r.Set(tt.source) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if r != tt.result { - t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) - } - } -} - -func TestXIDAssignTo(t *testing.T) { - var ui32 uint32 - var pui32 *uint32 - - simpleTests := []struct { - src pgtype.XID - dst interface{} - expected interface{} - }{ - {src: pgtype.XID{Uint: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.XID{Status: pgtype.Null}, dst: &pui32, expected: ((*uint32)(nil))}, - } - - for i, tt := range simpleTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - pointerAllocTests := []struct { - src pgtype.XID - dst interface{} - expected interface{} - }{ - {src: pgtype.XID{Uint: 42, Status: pgtype.Present}, dst: &pui32, expected: uint32(42)}, - } - - for i, tt := range pointerAllocTests { - err := tt.src.AssignTo(tt.dst) - if err != nil { - t.Errorf("%d: %v", i, err) - } - - if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected { - t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst) - } - } - - errorTests := []struct { - src pgtype.XID - dst interface{} - }{ - {src: pgtype.XID{Status: pgtype.Null}, dst: &ui32}, - } - - for i, tt := range errorTests { - err := tt.src.AssignTo(tt.dst) - if err == nil { - t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst) - } - } -} diff --git a/query.go b/query.go index e104e240..4d372e06 100644 --- a/query.go +++ b/query.go @@ -9,7 +9,7 @@ import ( errors "golang.org/x/xerrors" "github.com/jackc/pgconn" - "github.com/jackc/pgx/v4/pgtype" + "github.com/jackc/pgtype" ) // Rows is the result set returned from *Conn.Query. Rows must be closed before diff --git a/query_test.go b/query_test.go index bd94e091..7d799343 100644 --- a/query_test.go +++ b/query_test.go @@ -13,9 +13,9 @@ import ( "github.com/cockroachdb/apd" "github.com/jackc/pgconn" + "github.com/jackc/pgtype" + satori "github.com/jackc/pgtype/ext/satori-uuid" "github.com/jackc/pgx/v4" - "github.com/jackc/pgx/v4/pgtype" - satori "github.com/jackc/pgx/v4/pgtype/ext/satori-uuid" uuid "github.com/satori/go.uuid" "github.com/shopspring/decimal" errors "golang.org/x/xerrors" diff --git a/replication.go b/replication.go index 67102189..cad9ebbb 100644 --- a/replication.go +++ b/replication.go @@ -12,7 +12,7 @@ import ( "github.com/jackc/pgconn" "github.com/jackc/pgio" "github.com/jackc/pgproto3/v2" - "github.com/jackc/pgx/v4/pgtype" + "github.com/jackc/pgtype" ) const ( diff --git a/stdlib/sql.go b/stdlib/sql.go index 3868591c..5a49ce0b 100644 --- a/stdlib/sql.go +++ b/stdlib/sql.go @@ -83,8 +83,8 @@ import ( errors "golang.org/x/xerrors" "github.com/jackc/pgconn" + "github.com/jackc/pgtype" "github.com/jackc/pgx/v4" - "github.com/jackc/pgx/v4/pgtype" ) // oids that map to intrinsic database/sql types. These will be allowed to be diff --git a/values.go b/values.go index 9faf238d..d6a5b9fb 100644 --- a/values.go +++ b/values.go @@ -8,7 +8,7 @@ import ( "time" "github.com/jackc/pgio" - "github.com/jackc/pgx/v4/pgtype" + "github.com/jackc/pgtype" errors "golang.org/x/xerrors" )