From ec09cc4ccfe5b6b5e66856cdfa6b79bc7088925c Mon Sep 17 00:00:00 2001 From: Nikitin Aleksandr Date: Wed, 2 Jul 2025 14:51:33 +0300 Subject: [PATCH] =?UTF-8?q?=D1=81=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20AfterCon?= =?UTF-8?q?nect=5FNoNull?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 4 +- go.sum | 18 - postgres_pgx/timestamptz.go | 373 ++++++++++++++++++ postgres_pgx/write.go | 40 ++ postgres_pgxpool/postgres_pgxpool.go | 66 +++- .../jackc/pgx/v5/pgtype/zeronull/doc.go | 22 ++ .../jackc/pgx/v5/pgtype/zeronull/float8.go | 56 +++ .../jackc/pgx/v5/pgtype/zeronull/int.go | 154 ++++++++ .../jackc/pgx/v5/pgtype/zeronull/int.go.erb | 60 +++ .../pgx/v5/pgtype/zeronull/int_test.go.erb | 31 ++ .../jackc/pgx/v5/pgtype/zeronull/text.go | 49 +++ .../jackc/pgx/v5/pgtype/zeronull/timestamp.go | 67 ++++ .../pgx/v5/pgtype/zeronull/timestamptz.go | 67 ++++ .../jackc/pgx/v5/pgtype/zeronull/uuid.go | 62 +++ .../jackc/pgx/v5/pgtype/zeronull/zeronull.go | 17 + vendor/modules.txt | 1 + 16 files changed, 1058 insertions(+), 29 deletions(-) create mode 100644 postgres_pgx/timestamptz.go create mode 100644 postgres_pgx/write.go create mode 100644 vendor/github.com/jackc/pgx/v5/pgtype/zeronull/doc.go create mode 100644 vendor/github.com/jackc/pgx/v5/pgtype/zeronull/float8.go create mode 100644 vendor/github.com/jackc/pgx/v5/pgtype/zeronull/int.go create mode 100644 vendor/github.com/jackc/pgx/v5/pgtype/zeronull/int.go.erb create mode 100644 vendor/github.com/jackc/pgx/v5/pgtype/zeronull/int_test.go.erb create mode 100644 vendor/github.com/jackc/pgx/v5/pgtype/zeronull/text.go create mode 100644 vendor/github.com/jackc/pgx/v5/pgtype/zeronull/timestamp.go create mode 100644 vendor/github.com/jackc/pgx/v5/pgtype/zeronull/timestamptz.go create mode 100644 vendor/github.com/jackc/pgx/v5/pgtype/zeronull/uuid.go create mode 100644 vendor/github.com/jackc/pgx/v5/pgtype/zeronull/zeronull.go diff --git a/go.mod b/go.mod index 889116fb..2cf8b065 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/cockroachdb/pebble v1.1.2 github.com/denisenkom/go-mssqldb v0.12.3 github.com/dromara/carbon/v2 v2.6.1 + github.com/dustin/go-humanize v1.0.1 github.com/emersion/go-imap v1.2.1 github.com/emersion/go-message v0.18.1 github.com/go-faster/errors v0.7.1 @@ -37,6 +38,7 @@ require ( go.uber.org/zap v1.27.0 golang.org/x/crypto v0.32.0 golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c + golang.org/x/net v0.34.0 google.golang.org/protobuf v1.36.4 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gorm.io/driver/postgres v1.5.11 @@ -59,7 +61,6 @@ require ( github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect github.com/getsentry/sentry-go v0.30.0 // indirect github.com/go-faster/jx v1.1.0 // indirect @@ -121,7 +122,6 @@ require ( go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/net v0.34.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.30.0 // indirect diff --git a/go.sum b/go.sum index 91693d0b..56dc16b8 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,6 @@ github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+ github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/dromara/carbon/v2 v2.5.2 h1:GquNyA9Imda+LwS9FIzHhKg+foU2QPstH+S3idBRjKg= -github.com/dromara/carbon/v2 v2.5.2/go.mod h1:zyPlND2o27sKKkRmdgLbk/qYxkmmH6Z4eE8OoM0w3DM= github.com/dromara/carbon/v2 v2.6.1 h1:ExZPeH74ApLJ/nqJ+SGp1JSPFawvTDOCG3WSeqYl0mI= github.com/dromara/carbon/v2 v2.6.1/go.mod h1:Baj3A1uBBctJmpZWJd6/+WWnmIuY2pobR6IOpB6xigc= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -316,14 +314,8 @@ github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3i github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -gitlab.aescorp.ru/dsp_dev/claim/common/sync_exchange v0.0.33 h1:sLKDG4x4Ov72Nd9LKhj2rZ55OrHLaUgxupqCF27808I= -gitlab.aescorp.ru/dsp_dev/claim/common/sync_exchange v0.0.33/go.mod h1:+ZbwpurumFpi0GbiahOY4shV6WB2kguFxN486+76prs= -gitlab.aescorp.ru/dsp_dev/claim/common/sync_exchange v0.0.34 h1:1w3epO1MYB3GAHYDEPNlJWtrSDt+eL+dCX1IqTI0HEk= -gitlab.aescorp.ru/dsp_dev/claim/common/sync_exchange v0.0.34/go.mod h1:+ZbwpurumFpi0GbiahOY4shV6WB2kguFxN486+76prs= gitlab.aescorp.ru/dsp_dev/claim/common/sync_exchange v0.0.35 h1:Cgp9Q86ySYlx6l4YCrgR88gff9LKrF7TozSW/M9wQP8= gitlab.aescorp.ru/dsp_dev/claim/common/sync_exchange v0.0.35/go.mod h1:+ZbwpurumFpi0GbiahOY4shV6WB2kguFxN486+76prs= -gitlab.aescorp.ru/dsp_dev/claim/sync_service v1.2.242 h1:bS4Cq65lLs0LYOV9hzXW0Gv2lIqXi1I1TBzwhln4iWw= -gitlab.aescorp.ru/dsp_dev/claim/sync_service v1.2.242/go.mod h1:VB93+iYc+6r5Jka9gRgQksjjW2MHtDltXAsHVP6ZELc= gitlab.aescorp.ru/dsp_dev/claim/sync_service v1.2.268 h1:lPdwJ8heNvDu+5prMK2cqj+qd4IO43r6bc2kth+Y8Mo= gitlab.aescorp.ru/dsp_dev/claim/sync_service v1.2.268/go.mod h1:mleo1C4JFt0qkNRkDhfQsTYR+xUXpSXJXe3VopssGCU= go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= @@ -367,8 +359,6 @@ golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= -golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc= golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -405,8 +395,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -431,8 +419,6 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -459,8 +445,6 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -473,8 +457,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 h1:J1H9f+LEdWAfHcez/4cvaVBox7cOYT+IU6rgqj5x++8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489 h1:5bKytslY8ViY0Cj/ewmRtrWHW64bNF03cAatUUFCdFI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= diff --git a/postgres_pgx/timestamptz.go b/postgres_pgx/timestamptz.go new file mode 100644 index 00000000..afd02829 --- /dev/null +++ b/postgres_pgx/timestamptz.go @@ -0,0 +1,373 @@ +package postgres_pgx + +import ( + "database/sql/driver" + "encoding/binary" + "encoding/json" + "fmt" + "github.com/jackc/pgx/v5/pgtype" + "strings" + "time" +) + +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 TimestamptzScanner interface { + ScanTimestamptz(v Timestamptz) error +} + +type TimestamptzValuer interface { + TimestamptzValue() (Timestamptz, error) +} + +// Timestamptz represents the PostgreSQL timestamptz type. +type Timestamptz struct { + Time time.Time + InfinityModifier pgtype.InfinityModifier + Valid bool +} + +func (tstz *Timestamptz) ScanTimestamptz(v Timestamptz) error { + *tstz = v + return nil +} + +func (tstz Timestamptz) TimestamptzValue() (Timestamptz, error) { + return tstz, nil +} + +// Scan implements the database/sql Scanner interface. +func (tstz *Timestamptz) Scan(src any) error { + if src == nil { + *tstz = Timestamptz{} + return nil + } + + switch src := src.(type) { + case string: + return (&scanPlanTextTimestamptzToTimestamptzScanner{}).Scan([]byte(src), tstz) + case time.Time: + *tstz = Timestamptz{Time: src, Valid: true} + return nil + } + + return fmt.Errorf("cannot scan %T", src) +} + +// Value implements the database/sql/driver Valuer interface. +func (tstz Timestamptz) Value() (driver.Value, error) { + if !tstz.Valid { + return nil, nil + } + + if tstz.InfinityModifier != pgtype.Finite { + return tstz.InfinityModifier.String(), nil + } + return tstz.Time, nil +} + +func (tstz Timestamptz) MarshalJSON() ([]byte, error) { + if !tstz.Valid { + return []byte("null"), nil + } + + var s string + + switch tstz.InfinityModifier { + case pgtype.Finite: + s = tstz.Time.Format(time.RFC3339Nano) + case pgtype.Infinity: + s = "infinity" + case pgtype.NegativeInfinity: + s = "-infinity" + } + + return json.Marshal(s) +} + +func (tstz *Timestamptz) UnmarshalJSON(b []byte) error { + var s *string + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + if s == nil { + *tstz = Timestamptz{} + return nil + } + + switch *s { + case "infinity": + *tstz = Timestamptz{Valid: true, InfinityModifier: pgtype.Infinity} + case "-infinity": + *tstz = Timestamptz{Valid: true, InfinityModifier: -pgtype.Infinity} + default: + // PostgreSQL uses ISO 8601 for to_json function and casting from a string to timestamptz + tim, err := time.Parse(time.RFC3339Nano, *s) + if err != nil { + return err + } + + *tstz = Timestamptz{Time: tim, Valid: true} + } + + return nil +} + +type TimestamptzCodec struct { + // ScanLocation is the location to return scanned timestamptz values in. This does not change the instant in time that + // the timestamptz represents. + ScanLocation *time.Location +} + +func (*TimestamptzCodec) FormatSupported(format int16) bool { + return format == pgtype.TextFormatCode || format == pgtype.BinaryFormatCode +} + +func (*TimestamptzCodec) PreferredFormat() int16 { + return pgtype.BinaryFormatCode +} + +func (*TimestamptzCodec) PlanEncode(m *pgtype.Map, oid uint32, format int16, value any) pgtype.EncodePlan { + if _, ok := value.(TimestamptzValuer); !ok { + return nil + } + + switch format { + case pgtype.BinaryFormatCode: + return encodePlanTimestamptzCodecBinary{} + case pgtype.TextFormatCode: + return encodePlanTimestamptzCodecText{} + } + + return nil +} + +type encodePlanTimestamptzCodecBinary struct{} + +func (encodePlanTimestamptzCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) { + ts, err := value.(TimestamptzValuer).TimestamptzValue() + if err != nil { + return nil, err + } + + if !ts.Valid { + return nil, nil + } + + var microsecSinceY2K int64 + switch ts.InfinityModifier { + case pgtype.Finite: + microsecSinceUnixEpoch := ts.Time.Unix()*1000000 + int64(ts.Time.Nanosecond())/1000 + microsecSinceY2K = microsecSinceUnixEpoch - microsecFromUnixEpochToY2K + case pgtype.Infinity: + microsecSinceY2K = infinityMicrosecondOffset + case pgtype.NegativeInfinity: + microsecSinceY2K = negativeInfinityMicrosecondOffset + } + + buf = AppendInt64(buf, microsecSinceY2K) + + return buf, nil +} + +type encodePlanTimestamptzCodecText struct{} + +func (encodePlanTimestamptzCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) { + ts, err := value.(TimestamptzValuer).TimestamptzValue() + if err != nil { + return nil, err + } + + if !ts.Valid { + return nil, nil + } + + var s string + + switch ts.InfinityModifier { + case pgtype.Finite: + + t := ts.Time.UTC().Truncate(time.Microsecond) + + // Year 0000 is 1 BC + bc := false + if year := t.Year(); year <= 0 { + year = -year + 1 + t = time.Date(year, t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.UTC) + bc = true + } + + s = t.Format(pgTimestamptzSecondFormat) + + if bc { + s = s + " BC" + } + case pgtype.Infinity: + s = "infinity" + case pgtype.NegativeInfinity: + s = "-infinity" + } + + buf = append(buf, s...) + + return buf, nil +} + +func (c *TimestamptzCodec) PlanScan(m *pgtype.Map, oid uint32, format int16, target any) pgtype.ScanPlan { + + switch format { + case pgtype.BinaryFormatCode: + switch target.(type) { + case TimestamptzScanner: + return &scanPlanBinaryTimestamptzToTimestamptzScanner{location: c.ScanLocation} + } + case pgtype.TextFormatCode: + switch target.(type) { + case TimestamptzScanner: + return &scanPlanTextTimestamptzToTimestamptzScanner{location: c.ScanLocation} + } + } + + return nil +} + +type scanPlanBinaryTimestamptzToTimestamptzScanner struct{ location *time.Location } + +func (plan *scanPlanBinaryTimestamptzToTimestamptzScanner) Scan(src []byte, dst any) error { + scanner := (dst).(TimestamptzScanner) + + if src == nil { + return scanner.ScanTimestamptz(Timestamptz{}) + } + + if len(src) != 8 { + return fmt.Errorf("invalid length for timestamptz: %v", len(src)) + } + + var tstz Timestamptz + microsecSinceY2K := int64(binary.BigEndian.Uint64(src)) + + switch microsecSinceY2K { + case infinityMicrosecondOffset: + tstz = Timestamptz{Valid: true, InfinityModifier: pgtype.Infinity} + case negativeInfinityMicrosecondOffset: + tstz = Timestamptz{Valid: true, InfinityModifier: -pgtype.Infinity} + default: + tim := time.Unix( + microsecFromUnixEpochToY2K/1000000+microsecSinceY2K/1000000, + (microsecFromUnixEpochToY2K%1000000*1000)+(microsecSinceY2K%1000000*1000), + ) + if plan.location != nil { + tim = tim.In(plan.location) + } + tstz = Timestamptz{Time: tim, Valid: true} + } + + return scanner.ScanTimestamptz(tstz) +} + +type scanPlanTextTimestamptzToTimestamptzScanner struct{ location *time.Location } + +func (plan *scanPlanTextTimestamptzToTimestamptzScanner) Scan(src []byte, dst any) error { + scanner := (dst).(TimestamptzScanner) + + if src == nil { + return scanner.ScanTimestamptz(Timestamptz{}) + } + + var tstz Timestamptz + sbuf := string(src) + switch sbuf { + case "infinity": + tstz = Timestamptz{Valid: true, InfinityModifier: pgtype.Infinity} + case "-infinity": + tstz = Timestamptz{Valid: true, InfinityModifier: -pgtype.Infinity} + default: + bc := false + if strings.HasSuffix(sbuf, " BC") { + sbuf = sbuf[:len(sbuf)-3] + bc = true + } + + var format string + if len(sbuf) >= 9 && (sbuf[len(sbuf)-9] == '-' || sbuf[len(sbuf)-9] == '+') { + format = pgTimestamptzSecondFormat + } else if len(sbuf) >= 6 && (sbuf[len(sbuf)-6] == '-' || sbuf[len(sbuf)-6] == '+') { + format = pgTimestamptzMinuteFormat + } else { + format = pgTimestamptzHourFormat + } + + tim, err := time.Parse(format, sbuf) + if err != nil { + return err + } + + if bc { + year := -tim.Year() + 1 + tim = time.Date(year, tim.Month(), tim.Day(), tim.Hour(), tim.Minute(), tim.Second(), tim.Nanosecond(), tim.Location()) + } + + if plan.location != nil { + tim = tim.In(plan.location) + } + + tstz = Timestamptz{Time: tim, Valid: true} + } + + return scanner.ScanTimestamptz(tstz) +} + +func (c *TimestamptzCodec) DecodeDatabaseSQLValue(m *pgtype.Map, oid uint32, format int16, src []byte) (driver.Value, error) { + if src == nil { + return nil, nil + } + + var tstz Timestamptz + err := codecScan(c, m, oid, format, src, &tstz) + if err != nil { + return nil, err + } + + if tstz.InfinityModifier != pgtype.Finite { + return tstz.InfinityModifier.String(), nil + } + + return tstz.Time, nil +} + +func (c *TimestamptzCodec) DecodeValue(m *pgtype.Map, oid uint32, format int16, src []byte) (any, error) { + if src == nil { + return nil, nil + } + + var tstz Timestamptz + err := codecScan(c, m, oid, format, src, &tstz) + if err != nil { + return nil, err + } + + if tstz.InfinityModifier != pgtype.Finite { + return tstz.InfinityModifier, nil + } + + return tstz.Time, nil +} + +func codecScan(codec pgtype.Codec, m *pgtype.Map, oid uint32, format int16, src []byte, dst any) error { + scanPlan := codec.PlanScan(m, oid, format, dst) + if scanPlan == nil { + return fmt.Errorf("PlanScan did not find a plan") + } + return scanPlan.Scan(src, dst) +} diff --git a/postgres_pgx/write.go b/postgres_pgx/write.go new file mode 100644 index 00000000..6e11ea72 --- /dev/null +++ b/postgres_pgx/write.go @@ -0,0 +1,40 @@ +package postgres_pgx + +import "encoding/binary" + +func AppendUint16(buf []byte, n uint16) []byte { + wp := len(buf) + buf = append(buf, 0, 0) + binary.BigEndian.PutUint16(buf[wp:], n) + return buf +} + +func AppendUint32(buf []byte, n uint32) []byte { + wp := len(buf) + buf = append(buf, 0, 0, 0, 0) + binary.BigEndian.PutUint32(buf[wp:], n) + return buf +} + +func AppendUint64(buf []byte, n uint64) []byte { + wp := len(buf) + buf = append(buf, 0, 0, 0, 0, 0, 0, 0, 0) + binary.BigEndian.PutUint64(buf[wp:], n) + return buf +} + +func AppendInt16(buf []byte, n int16) []byte { + return AppendUint16(buf, uint16(n)) +} + +func AppendInt32(buf []byte, n int32) []byte { + return AppendUint32(buf, uint32(n)) +} + +func AppendInt64(buf []byte, n int64) []byte { + return AppendUint64(buf, uint64(n)) +} + +func SetInt32(buf []byte, n int32) { + binary.BigEndian.PutUint32(buf, uint32(n)) +} diff --git a/postgres_pgxpool/postgres_pgxpool.go b/postgres_pgxpool/postgres_pgxpool.go index f64da11f..e32b78e2 100644 --- a/postgres_pgxpool/postgres_pgxpool.go +++ b/postgres_pgxpool/postgres_pgxpool.go @@ -11,6 +11,7 @@ import ( "github.com/ManyakRus/starter/port_checker" "github.com/ManyakRus/starter/postgres_pgx" "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgxpool" "strings" "time" @@ -45,6 +46,7 @@ type SettingsINI struct { DB_SCHEMA string DB_USER string DB_PASSWORD string + NoNUll bool } // TextConnBusy - текст ошибки "conn busy" @@ -53,7 +55,7 @@ const TextConnBusy = "conn busy" // timeOutSeconds - время ожидания для Ping() const timeOutSeconds = 1 -// Connect_err - подключается к базе данных +// Connect - подключается к базе данных func Connect() { if Settings.DB_HOST == "" { @@ -67,6 +69,30 @@ func Connect() { } +// Connect_err - подключается к базе данных, возвращает ошибку +func Connect_err() error { + var err error + err = Connect_WithApplicationName_err("") + + return err +} + +// Connect_NoNull - подключается к базе данных +// запросы вместо null возвращают значение по умолчанию (пока только дата) +func Connect_NoNull() { + + if Settings.DB_HOST == "" { + FillSettings() + } + Settings.NoNUll = true + + port_checker.CheckPort(Settings.DB_HOST, Settings.DB_PORT) + + err := Connect_err() + LogInfo_Connected(err) + +} + // LogInfo_Connected - выводит сообщение в Лог, или паника при ошибке func LogInfo_Connected(err error) { if err != nil { @@ -77,14 +103,6 @@ func LogInfo_Connected(err error) { } -// Connect_err - подключается к базе данных -func Connect_err() error { - var err error - err = Connect_WithApplicationName_err("") - - return err -} - // Connect_WithApplicationName - подключается к базе данных, с указанием имени приложения func Connect_WithApplicationName(ApplicationName string) { err := Connect_WithApplicationName_err(ApplicationName) @@ -113,6 +131,9 @@ func Connect_WithApplicationName_err(ApplicationName string) error { // config, err := pgxpool.ParseConfig(databaseUrl) + if Settings.NoNUll == true { + config.AfterConnect = AfterConnect_NoNull + } //config.PreferSimpleProtocol = true //для мульти-запросов PgxPool = nil PgxPool, err = pgxpool.NewWithConfig(ctx, config) @@ -311,6 +332,22 @@ func Start_ctx(ctx *context.Context, WaitGroup *sync.WaitGroup) error { return err } +// Start_NoNull - делает соединение с БД, отключение и др. +// запросы вместо null возвращают значение по умолчанию (пока только дата) +func Start_NoNull(ApplicationName string) { + Settings.NoNUll = true + + err := Connect_WithApplicationName_err(ApplicationName) + LogInfo_Connected(err) + + stopapp.GetWaitGroup_Main().Add(1) + go WaitStop() + + stopapp.GetWaitGroup_Main().Add(1) + go ping_go() + +} + // Start - делает соединение с БД, отключение и др. func Start(ApplicationName string) { err := Connect_WithApplicationName_err(ApplicationName) @@ -559,3 +596,14 @@ func ReplaceSchemaName(TextSQL, SchemaNameFrom string) string { return Otvet } + +func AfterConnect_NoNull(ctx context.Context, conn *pgx.Conn) error { + // Регистрируем zeronull обработчики для нужных типов + conn.TypeMap().RegisterType(&pgtype.Type{ + Name: "timestamptz", + OID: pgtype.TimestamptzOID, + Codec: &postgres_pgx.TimestamptzCodec{}, + }) + + return nil +} diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/doc.go b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/doc.go new file mode 100644 index 00000000..78a52307 --- /dev/null +++ b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/doc.go @@ -0,0 +1,22 @@ +// Package zeronull contains types that automatically convert between database NULLs and Go zero values. +/* +Sometimes the distinction between a zero value and a NULL value is not useful at the application level. For example, +in PostgreSQL an empty string may be stored as NULL. There is usually no application level distinction between an +empty string and a NULL string. Package zeronull implements types that seamlessly convert between PostgreSQL NULL and +the zero value. + +It is recommended to convert types at usage time rather than instantiate these types directly. In the example below, +middlename would be stored as a NULL. + + firstname := "John" + middlename := "" + lastname := "Smith" + _, err := conn.Exec( + ctx, + "insert into people(firstname, middlename, lastname) values($1, $2, $3)", + zeronull.Text(firstname), + zeronull.Text(middlename), + zeronull.Text(lastname), + ) +*/ +package zeronull diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/float8.go b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/float8.go new file mode 100644 index 00000000..08fa169e --- /dev/null +++ b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/float8.go @@ -0,0 +1,56 @@ +package zeronull + +import ( + "database/sql/driver" + + "github.com/jackc/pgx/v5/pgtype" +) + +type Float8 float64 + +func (Float8) SkipUnderlyingTypePlan() {} + +// ScanFloat64 implements the Float64Scanner interface. +func (f *Float8) ScanFloat64(n pgtype.Float8) error { + if !n.Valid { + *f = 0 + return nil + } + + *f = Float8(n.Float64) + + return nil +} + +func (f Float8) Float64Value() (pgtype.Float8, error) { + if f == 0 { + return pgtype.Float8{}, nil + } + return pgtype.Float8{Float64: float64(f), Valid: true}, nil +} + +// Scan implements the database/sql Scanner interface. +func (f *Float8) Scan(src any) error { + if src == nil { + *f = 0 + return nil + } + + var nullable pgtype.Float8 + err := nullable.Scan(src) + if err != nil { + return err + } + + *f = Float8(nullable.Float64) + + return nil +} + +// Value implements the database/sql/driver Valuer interface. +func (f Float8) Value() (driver.Value, error) { + if f == 0 { + return nil, nil + } + return float64(f), nil +} diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/int.go b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/int.go new file mode 100644 index 00000000..4fec8a1a --- /dev/null +++ b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/int.go @@ -0,0 +1,154 @@ +// Do not edit. Generated from pgtype/zeronull/int.go.erb +package zeronull + +import ( + "database/sql/driver" + "fmt" + "math" + + "github.com/jackc/pgx/v5/pgtype" +) + +type Int2 int16 + +func (Int2) SkipUnderlyingTypePlan() {} + +// ScanInt64 implements the Int64Scanner interface. +func (dst *Int2) ScanInt64(n int64, valid bool) error { + if !valid { + *dst = 0 + return nil + } + + if n < math.MinInt16 { + return fmt.Errorf("%d is greater than maximum value for Int2", n) + } + if n > math.MaxInt16 { + return fmt.Errorf("%d is greater than maximum value for Int2", n) + } + *dst = Int2(n) + + return nil +} + +// Scan implements the database/sql Scanner interface. +func (dst *Int2) Scan(src any) error { + if src == nil { + *dst = 0 + return nil + } + + var nullable pgtype.Int2 + err := nullable.Scan(src) + if err != nil { + return err + } + + *dst = Int2(nullable.Int16) + + return nil +} + +// Value implements the database/sql/driver Valuer interface. +func (src Int2) Value() (driver.Value, error) { + if src == 0 { + return nil, nil + } + return int64(src), nil +} + +type Int4 int32 + +func (Int4) SkipUnderlyingTypePlan() {} + +// ScanInt64 implements the Int64Scanner interface. +func (dst *Int4) ScanInt64(n int64, valid bool) error { + if !valid { + *dst = 0 + return nil + } + + if n < math.MinInt32 { + return fmt.Errorf("%d is greater than maximum value for Int4", n) + } + if n > math.MaxInt32 { + return fmt.Errorf("%d is greater than maximum value for Int4", n) + } + *dst = Int4(n) + + return nil +} + +// Scan implements the database/sql Scanner interface. +func (dst *Int4) Scan(src any) error { + if src == nil { + *dst = 0 + return nil + } + + var nullable pgtype.Int4 + err := nullable.Scan(src) + if err != nil { + return err + } + + *dst = Int4(nullable.Int32) + + return nil +} + +// Value implements the database/sql/driver Valuer interface. +func (src Int4) Value() (driver.Value, error) { + if src == 0 { + return nil, nil + } + return int64(src), nil +} + +type Int8 int64 + +func (Int8) SkipUnderlyingTypePlan() {} + +// ScanInt64 implements the Int64Scanner interface. +func (dst *Int8) ScanInt64(n int64, valid bool) error { + if !valid { + *dst = 0 + return nil + } + + if n < math.MinInt64 { + return fmt.Errorf("%d is greater than maximum value for Int8", n) + } + if n > math.MaxInt64 { + return fmt.Errorf("%d is greater than maximum value for Int8", n) + } + *dst = Int8(n) + + return nil +} + +// Scan implements the database/sql Scanner interface. +func (dst *Int8) Scan(src any) error { + if src == nil { + *dst = 0 + return nil + } + + var nullable pgtype.Int8 + err := nullable.Scan(src) + if err != nil { + return err + } + + *dst = Int8(nullable.Int64) + + return nil +} + +// Value implements the database/sql/driver Valuer interface. +func (src Int8) Value() (driver.Value, error) { + if src == 0 { + return nil, nil + } + return int64(src), nil +} diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/int.go.erb b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/int.go.erb new file mode 100644 index 00000000..b51cba12 --- /dev/null +++ b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/int.go.erb @@ -0,0 +1,60 @@ +package zeronull + +import ( + "database/sql/driver" + "fmt" + "math" + + "github.com/jackc/pgx/v5/pgtype" +) + +<% [2, 4, 8].each do |pg_byte_size| %> +<% pg_bit_size = pg_byte_size * 8 %> +type Int<%= pg_byte_size %> int<%= pg_bit_size %> + +func (Int<%= pg_byte_size %>) SkipUnderlyingTypePlan() {} + +// ScanInt64 implements the Int64Scanner interface. +func (dst *Int<%= pg_byte_size %>) ScanInt64(n int64, valid bool) error { + if !valid { + *dst = 0 + return nil + } + + if n < math.MinInt<%= pg_bit_size %> { + return fmt.Errorf("%d is greater than maximum value for Int<%= pg_byte_size %>", n) + } + if n > math.MaxInt<%= pg_bit_size %> { + return fmt.Errorf("%d is greater than maximum value for Int<%= pg_byte_size %>", n) + } + *dst = Int<%= pg_byte_size %>(n) + + return nil +} + +// Scan implements the database/sql Scanner interface. +func (dst *Int<%= pg_byte_size %>) Scan(src any) error { + if src == nil { + *dst = 0 + return nil + } + + var nullable pgtype.Int<%= pg_byte_size %> + err := nullable.Scan(src) + if err != nil { + return err + } + + *dst = Int<%= pg_byte_size %>(nullable.Int<%= pg_bit_size %>) + + return nil +} + +// Value implements the database/sql/driver Valuer interface. +func (src Int<%= pg_byte_size %>) Value() (driver.Value, error) { + if src == 0 { + return nil, nil + } + return int64(src), nil +} +<% end %> diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/int_test.go.erb b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/int_test.go.erb new file mode 100644 index 00000000..c0f72ef4 --- /dev/null +++ b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/int_test.go.erb @@ -0,0 +1,31 @@ +package zeronull_test + +import ( + "testing" + + "github.com/jackc/pgx/v5/pgtype/testutil" + "github.com/jackc/pgx/v5/pgtype/zeronull" +) + +<% [2, 4, 8].each do |pg_byte_size| %> +<% pg_bit_size = pg_byte_size * 8 %> +func TestInt<%= pg_byte_size %>Transcode(t *testing.T) { + pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "int<%= pg_byte_size %>", []pgxtest.ValueRoundTripTest{ + { + (zeronull.Int<%= pg_byte_size %>)(1), + new(zeronull.Int<%= pg_byte_size %>), + isExpectedEq((zeronull.Int<%= pg_byte_size %>)(1)), + }, + { + nil, + new(zeronull.Int<%= pg_byte_size %>), + isExpectedEq((zeronull.Int<%= pg_byte_size %>)(0)), + }, + { + (zeronull.Int<%= pg_byte_size %>)(0), + new(any), + isExpectedEq(nil), + }, + }) +} +<% end %> diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/text.go b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/text.go new file mode 100644 index 00000000..4ba51fa9 --- /dev/null +++ b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/text.go @@ -0,0 +1,49 @@ +package zeronull + +import ( + "database/sql/driver" + + "github.com/jackc/pgx/v5/pgtype" +) + +type Text string + +func (Text) SkipUnderlyingTypePlan() {} + +// ScanText implements the TextScanner interface. +func (dst *Text) ScanText(v pgtype.Text) error { + if !v.Valid { + *dst = "" + return nil + } + + *dst = Text(v.String) + + return nil +} + +// Scan implements the database/sql Scanner interface. +func (dst *Text) Scan(src any) error { + if src == nil { + *dst = "" + return nil + } + + var nullable pgtype.Text + err := nullable.Scan(src) + if err != nil { + return err + } + + *dst = Text(nullable.String) + + return nil +} + +// Value implements the database/sql/driver Valuer interface. +func (src Text) Value() (driver.Value, error) { + if src == "" { + return nil, nil + } + return string(src), nil +} diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/timestamp.go b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/timestamp.go new file mode 100644 index 00000000..1697c420 --- /dev/null +++ b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/timestamp.go @@ -0,0 +1,67 @@ +package zeronull + +import ( + "database/sql/driver" + "fmt" + "time" + + "github.com/jackc/pgx/v5/pgtype" +) + +type Timestamp time.Time + +func (Timestamp) SkipUnderlyingTypePlan() {} + +func (ts *Timestamp) ScanTimestamp(v pgtype.Timestamp) error { + if !v.Valid { + *ts = Timestamp{} + return nil + } + + switch v.InfinityModifier { + case pgtype.Finite: + *ts = Timestamp(v.Time) + return nil + case pgtype.Infinity: + return fmt.Errorf("cannot scan Infinity into *time.Time") + case pgtype.NegativeInfinity: + return fmt.Errorf("cannot scan -Infinity into *time.Time") + default: + return fmt.Errorf("invalid InfinityModifier: %v", v.InfinityModifier) + } +} + +func (ts Timestamp) TimestampValue() (pgtype.Timestamp, error) { + if time.Time(ts).IsZero() { + return pgtype.Timestamp{}, nil + } + + return pgtype.Timestamp{Time: time.Time(ts), Valid: true}, nil +} + +// Scan implements the database/sql Scanner interface. +func (ts *Timestamp) Scan(src any) error { + if src == nil { + *ts = Timestamp{} + return nil + } + + var nullable pgtype.Timestamp + err := nullable.Scan(src) + if err != nil { + return err + } + + *ts = Timestamp(nullable.Time) + + return nil +} + +// Value implements the database/sql/driver Valuer interface. +func (ts Timestamp) Value() (driver.Value, error) { + if time.Time(ts).IsZero() { + return nil, nil + } + + return time.Time(ts), nil +} diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/timestamptz.go b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/timestamptz.go new file mode 100644 index 00000000..f7ff19d2 --- /dev/null +++ b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/timestamptz.go @@ -0,0 +1,67 @@ +package zeronull + +import ( + "database/sql/driver" + "fmt" + "time" + + "github.com/jackc/pgx/v5/pgtype" +) + +type Timestamptz time.Time + +func (Timestamptz) SkipUnderlyingTypePlan() {} + +func (ts *Timestamptz) ScanTimestamptz(v pgtype.Timestamptz) error { + if !v.Valid { + *ts = Timestamptz{} + return nil + } + + switch v.InfinityModifier { + case pgtype.Finite: + *ts = Timestamptz(v.Time) + return nil + case pgtype.Infinity: + return fmt.Errorf("cannot scan Infinity into *time.Time") + case pgtype.NegativeInfinity: + return fmt.Errorf("cannot scan -Infinity into *time.Time") + default: + return fmt.Errorf("invalid InfinityModifier: %v", v.InfinityModifier) + } +} + +func (ts Timestamptz) TimestamptzValue() (pgtype.Timestamptz, error) { + if time.Time(ts).IsZero() { + return pgtype.Timestamptz{}, nil + } + + return pgtype.Timestamptz{Time: time.Time(ts), Valid: true}, nil +} + +// Scan implements the database/sql Scanner interface. +func (ts *Timestamptz) Scan(src any) error { + if src == nil { + *ts = Timestamptz{} + return nil + } + + var nullable pgtype.Timestamptz + err := nullable.Scan(src) + if err != nil { + return err + } + + *ts = Timestamptz(nullable.Time) + + return nil +} + +// Value implements the database/sql/driver Valuer interface. +func (ts Timestamptz) Value() (driver.Value, error) { + if time.Time(ts).IsZero() { + return nil, nil + } + + return time.Time(ts), nil +} diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/uuid.go b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/uuid.go new file mode 100644 index 00000000..d88be84d --- /dev/null +++ b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/uuid.go @@ -0,0 +1,62 @@ +package zeronull + +import ( + "database/sql/driver" + + "github.com/jackc/pgx/v5/pgtype" +) + +type UUID [16]byte + +func (UUID) SkipUnderlyingTypePlan() {} + +// ScanUUID implements the UUIDScanner interface. +func (u *UUID) ScanUUID(v pgtype.UUID) error { + if !v.Valid { + *u = UUID{} + return nil + } + + *u = UUID(v.Bytes) + + return nil +} + +func (u UUID) UUIDValue() (pgtype.UUID, error) { + if u == (UUID{}) { + return pgtype.UUID{}, nil + } + return pgtype.UUID{Bytes: u, Valid: true}, nil +} + +// Scan implements the database/sql Scanner interface. +func (u *UUID) Scan(src any) error { + if src == nil { + *u = UUID{} + return nil + } + + var nullable pgtype.UUID + err := nullable.Scan(src) + if err != nil { + return err + } + + *u = UUID(nullable.Bytes) + + return nil +} + +// Value implements the database/sql/driver Valuer interface. +func (u UUID) Value() (driver.Value, error) { + if u == (UUID{}) { + return nil, nil + } + + buf, err := pgtype.UUIDCodec{}.PlanEncode(nil, pgtype.UUIDOID, pgtype.TextFormatCode, u).Encode(u, nil) + if err != nil { + return nil, err + } + + return string(buf), nil +} diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/zeronull.go b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/zeronull.go new file mode 100644 index 00000000..bba7b423 --- /dev/null +++ b/vendor/github.com/jackc/pgx/v5/pgtype/zeronull/zeronull.go @@ -0,0 +1,17 @@ +package zeronull + +import ( + "github.com/jackc/pgx/v5/pgtype" +) + +// Register registers the zeronull types so they can be used in query exec modes that do not know the server OIDs. +func Register(m *pgtype.Map) { + m.RegisterDefaultPgType(Float8(0), "float8") + m.RegisterDefaultPgType(Int2(0), "int2") + m.RegisterDefaultPgType(Int4(0), "int4") + m.RegisterDefaultPgType(Int8(0), "int8") + m.RegisterDefaultPgType(Text(""), "text") + m.RegisterDefaultPgType(Timestamp{}, "timestamp") + m.RegisterDefaultPgType(Timestamptz{}, "timestamptz") + m.RegisterDefaultPgType(UUID{}, "uuid") +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 113b73d7..a23b2a4c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -312,6 +312,7 @@ github.com/jackc/pgx/v5/pgconn/ctxwatch github.com/jackc/pgx/v5/pgconn/internal/bgreader github.com/jackc/pgx/v5/pgproto3 github.com/jackc/pgx/v5/pgtype +github.com/jackc/pgx/v5/pgtype/zeronull github.com/jackc/pgx/v5/pgxpool github.com/jackc/pgx/v5/stdlib # github.com/jackc/puddle/v2 v2.2.2