private/dbutil/pgutil: move type helpers to separate file
Change-Id: I6357894434b565b9e48dbe5ac9b87542dcd5fc38
This commit is contained in:
parent
4eb12eb70b
commit
1d28dc314c
@ -6,14 +6,11 @@ package pgutil
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/jackc/pgtype"
|
|
||||||
"github.com/jackc/pgx/v4"
|
"github.com/jackc/pgx/v4"
|
||||||
"github.com/spacemonkeygo/monkit/v3"
|
"github.com/spacemonkeygo/monkit/v3"
|
||||||
"github.com/zeebo/errs"
|
"github.com/zeebo/errs"
|
||||||
|
|
||||||
"storj.io/common/storj"
|
|
||||||
"storj.io/storj/private/dbutil"
|
"storj.io/storj/private/dbutil"
|
||||||
"storj.io/storj/private/dbutil/dbschema"
|
"storj.io/storj/private/dbutil/dbschema"
|
||||||
"storj.io/storj/private/dbutil/pgutil/pgerrcode"
|
"storj.io/storj/private/dbutil/pgutil/pgerrcode"
|
||||||
@ -115,130 +112,6 @@ func IsConstraintError(err error) bool {
|
|||||||
return strings.HasPrefix(errCode, pgErrorClassConstraintViolation)
|
return strings.HasPrefix(errCode, pgErrorClassConstraintViolation)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following XArray() helper methods exist alongside similar methods in the
|
|
||||||
// jackc/pgtype library. The difference with the methods in pgtype is that they
|
|
||||||
// will accept any of a wide range of types. That is nice, but it comes with
|
|
||||||
// the potential that someone might pass in an invalid type; thus, those
|
|
||||||
// methods have to return (*pgtype.XArray, error).
|
|
||||||
//
|
|
||||||
// The methods here do not need to return an error because they require passing
|
|
||||||
// in the correct type to begin with.
|
|
||||||
//
|
|
||||||
// An alternative implementation for the following methods might look like
|
|
||||||
// calls to pgtype.ByteaArray() followed by `if err != nil { panic }` blocks.
|
|
||||||
// That would probably be ok, but we decided on this approach, as it ought to
|
|
||||||
// require fewer allocations and less time, in addition to having no error
|
|
||||||
// return.
|
|
||||||
|
|
||||||
// ByteaArray returns an object usable by pg drivers for passing a [][]byte slice
|
|
||||||
// into a database as type BYTEA[].
|
|
||||||
func ByteaArray(bytesArray [][]byte) *pgtype.ByteaArray {
|
|
||||||
pgtypeByteaArray := make([]pgtype.Bytea, len(bytesArray))
|
|
||||||
for i, byteSlice := range bytesArray {
|
|
||||||
pgtypeByteaArray[i].Bytes = byteSlice
|
|
||||||
pgtypeByteaArray[i].Status = pgtype.Present
|
|
||||||
}
|
|
||||||
return &pgtype.ByteaArray{
|
|
||||||
Elements: pgtypeByteaArray,
|
|
||||||
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(bytesArray)), LowerBound: 1}},
|
|
||||||
Status: pgtype.Present,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TextArray returns an object usable by pg drivers for passing a []string slice
|
|
||||||
// into a database as type TEXT[].
|
|
||||||
func TextArray(stringSlice []string) *pgtype.TextArray {
|
|
||||||
pgtypeTextArray := make([]pgtype.Text, len(stringSlice))
|
|
||||||
for i, s := range stringSlice {
|
|
||||||
pgtypeTextArray[i].String = s
|
|
||||||
pgtypeTextArray[i].Status = pgtype.Present
|
|
||||||
}
|
|
||||||
return &pgtype.TextArray{
|
|
||||||
Elements: pgtypeTextArray,
|
|
||||||
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(stringSlice)), LowerBound: 1}},
|
|
||||||
Status: pgtype.Present,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TimestampTZArray returns an object usable by pg drivers for passing a []time.Time
|
|
||||||
// slice into a database as type TIMESTAMPTZ[].
|
|
||||||
func TimestampTZArray(timeSlice []time.Time) *pgtype.TimestamptzArray {
|
|
||||||
pgtypeTimestamptzArray := make([]pgtype.Timestamptz, len(timeSlice))
|
|
||||||
for i, t := range timeSlice {
|
|
||||||
pgtypeTimestamptzArray[i].Time = t
|
|
||||||
pgtypeTimestamptzArray[i].Status = pgtype.Present
|
|
||||||
}
|
|
||||||
return &pgtype.TimestamptzArray{
|
|
||||||
Elements: pgtypeTimestamptzArray,
|
|
||||||
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(timeSlice)), LowerBound: 1}},
|
|
||||||
Status: pgtype.Present,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int4Array returns an object usable by pg drivers for passing a []int32 slice
|
|
||||||
// into a database as type INT4[].
|
|
||||||
func Int4Array(ints []int32) *pgtype.Int4Array {
|
|
||||||
pgtypeInt4Array := make([]pgtype.Int4, len(ints))
|
|
||||||
for i, someInt := range ints {
|
|
||||||
pgtypeInt4Array[i].Int = someInt
|
|
||||||
pgtypeInt4Array[i].Status = pgtype.Present
|
|
||||||
}
|
|
||||||
return &pgtype.Int4Array{
|
|
||||||
Elements: pgtypeInt4Array,
|
|
||||||
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(ints)), LowerBound: 1}},
|
|
||||||
Status: pgtype.Present,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int8Array returns an object usable by pg drivers for passing a []int64 slice
|
|
||||||
// into a database as type INT8[].
|
|
||||||
func Int8Array(bigInts []int64) *pgtype.Int8Array {
|
|
||||||
pgtypeInt8Array := make([]pgtype.Int8, len(bigInts))
|
|
||||||
for i, bigInt := range bigInts {
|
|
||||||
pgtypeInt8Array[i].Int = bigInt
|
|
||||||
pgtypeInt8Array[i].Status = pgtype.Present
|
|
||||||
}
|
|
||||||
return &pgtype.Int8Array{
|
|
||||||
Elements: pgtypeInt8Array,
|
|
||||||
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(bigInts)), LowerBound: 1}},
|
|
||||||
Status: pgtype.Present,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float8Array returns an object usable by pg drivers for passing a []float64 slice
|
|
||||||
// into a database as type FLOAT8[].
|
|
||||||
func Float8Array(floats []float64) *pgtype.Float8Array {
|
|
||||||
pgtypeFloat8Array := make([]pgtype.Float8, len(floats))
|
|
||||||
for i, someFloat := range floats {
|
|
||||||
pgtypeFloat8Array[i].Float = someFloat
|
|
||||||
pgtypeFloat8Array[i].Status = pgtype.Present
|
|
||||||
}
|
|
||||||
return &pgtype.Float8Array{
|
|
||||||
Elements: pgtypeFloat8Array,
|
|
||||||
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(floats)), LowerBound: 1}},
|
|
||||||
Status: pgtype.Present,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeIDArray returns an object usable by pg drivers for passing a []storj.NodeID
|
|
||||||
// slice into a database as type BYTEA[].
|
|
||||||
func NodeIDArray(nodeIDs []storj.NodeID) *pgtype.ByteaArray {
|
|
||||||
if nodeIDs == nil {
|
|
||||||
return &pgtype.ByteaArray{Status: pgtype.Null}
|
|
||||||
}
|
|
||||||
pgtypeByteaArray := make([]pgtype.Bytea, len(nodeIDs))
|
|
||||||
for i, nodeID := range nodeIDs {
|
|
||||||
nodeIDCopy := nodeID
|
|
||||||
pgtypeByteaArray[i].Bytes = nodeIDCopy[:]
|
|
||||||
pgtypeByteaArray[i].Status = pgtype.Present
|
|
||||||
}
|
|
||||||
return &pgtype.ByteaArray{
|
|
||||||
Elements: pgtypeByteaArray,
|
|
||||||
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(nodeIDs)), LowerBound: 1}},
|
|
||||||
Status: pgtype.Present,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// QuoteIdentifier quotes an identifier for use in an interpolated SQL string.
|
// QuoteIdentifier quotes an identifier for use in an interpolated SQL string.
|
||||||
func QuoteIdentifier(ident string) string {
|
func QuoteIdentifier(ident string) string {
|
||||||
return pgx.Identifier{ident}.Sanitize()
|
return pgx.Identifier{ident}.Sanitize()
|
||||||
|
136
private/dbutil/pgutil/types.go
Normal file
136
private/dbutil/pgutil/types.go
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
// Copyright (C) 2021 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
package pgutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgtype"
|
||||||
|
|
||||||
|
"storj.io/common/storj"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The following XArray() helper methods exist alongside similar methods in the
|
||||||
|
// jackc/pgtype library. The difference with the methods in pgtype is that they
|
||||||
|
// will accept any of a wide range of types. That is nice, but it comes with
|
||||||
|
// the potential that someone might pass in an invalid type; thus, those
|
||||||
|
// methods have to return (*pgtype.XArray, error).
|
||||||
|
//
|
||||||
|
// The methods here do not need to return an error because they require passing
|
||||||
|
// in the correct type to begin with.
|
||||||
|
//
|
||||||
|
// An alternative implementation for the following methods might look like
|
||||||
|
// calls to pgtype.ByteaArray() followed by `if err != nil { panic }` blocks.
|
||||||
|
// That would probably be ok, but we decided on this approach, as it ought to
|
||||||
|
// require fewer allocations and less time, in addition to having no error
|
||||||
|
// return.
|
||||||
|
|
||||||
|
// ByteaArray returns an object usable by pg drivers for passing a [][]byte slice
|
||||||
|
// into a database as type BYTEA[].
|
||||||
|
func ByteaArray(bytesArray [][]byte) *pgtype.ByteaArray {
|
||||||
|
pgtypeByteaArray := make([]pgtype.Bytea, len(bytesArray))
|
||||||
|
for i, byteSlice := range bytesArray {
|
||||||
|
pgtypeByteaArray[i].Bytes = byteSlice
|
||||||
|
pgtypeByteaArray[i].Status = pgtype.Present
|
||||||
|
}
|
||||||
|
return &pgtype.ByteaArray{
|
||||||
|
Elements: pgtypeByteaArray,
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(bytesArray)), LowerBound: 1}},
|
||||||
|
Status: pgtype.Present,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextArray returns an object usable by pg drivers for passing a []string slice
|
||||||
|
// into a database as type TEXT[].
|
||||||
|
func TextArray(stringSlice []string) *pgtype.TextArray {
|
||||||
|
pgtypeTextArray := make([]pgtype.Text, len(stringSlice))
|
||||||
|
for i, s := range stringSlice {
|
||||||
|
pgtypeTextArray[i].String = s
|
||||||
|
pgtypeTextArray[i].Status = pgtype.Present
|
||||||
|
}
|
||||||
|
return &pgtype.TextArray{
|
||||||
|
Elements: pgtypeTextArray,
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(stringSlice)), LowerBound: 1}},
|
||||||
|
Status: pgtype.Present,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimestampTZArray returns an object usable by pg drivers for passing a []time.Time
|
||||||
|
// slice into a database as type TIMESTAMPTZ[].
|
||||||
|
func TimestampTZArray(timeSlice []time.Time) *pgtype.TimestamptzArray {
|
||||||
|
pgtypeTimestamptzArray := make([]pgtype.Timestamptz, len(timeSlice))
|
||||||
|
for i, t := range timeSlice {
|
||||||
|
pgtypeTimestamptzArray[i].Time = t
|
||||||
|
pgtypeTimestamptzArray[i].Status = pgtype.Present
|
||||||
|
}
|
||||||
|
return &pgtype.TimestamptzArray{
|
||||||
|
Elements: pgtypeTimestamptzArray,
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(timeSlice)), LowerBound: 1}},
|
||||||
|
Status: pgtype.Present,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int4Array returns an object usable by pg drivers for passing a []int32 slice
|
||||||
|
// into a database as type INT4[].
|
||||||
|
func Int4Array(ints []int32) *pgtype.Int4Array {
|
||||||
|
pgtypeInt4Array := make([]pgtype.Int4, len(ints))
|
||||||
|
for i, someInt := range ints {
|
||||||
|
pgtypeInt4Array[i].Int = someInt
|
||||||
|
pgtypeInt4Array[i].Status = pgtype.Present
|
||||||
|
}
|
||||||
|
return &pgtype.Int4Array{
|
||||||
|
Elements: pgtypeInt4Array,
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(ints)), LowerBound: 1}},
|
||||||
|
Status: pgtype.Present,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int8Array returns an object usable by pg drivers for passing a []int64 slice
|
||||||
|
// into a database as type INT8[].
|
||||||
|
func Int8Array(bigInts []int64) *pgtype.Int8Array {
|
||||||
|
pgtypeInt8Array := make([]pgtype.Int8, len(bigInts))
|
||||||
|
for i, bigInt := range bigInts {
|
||||||
|
pgtypeInt8Array[i].Int = bigInt
|
||||||
|
pgtypeInt8Array[i].Status = pgtype.Present
|
||||||
|
}
|
||||||
|
return &pgtype.Int8Array{
|
||||||
|
Elements: pgtypeInt8Array,
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(bigInts)), LowerBound: 1}},
|
||||||
|
Status: pgtype.Present,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float8Array returns an object usable by pg drivers for passing a []float64 slice
|
||||||
|
// into a database as type FLOAT8[].
|
||||||
|
func Float8Array(floats []float64) *pgtype.Float8Array {
|
||||||
|
pgtypeFloat8Array := make([]pgtype.Float8, len(floats))
|
||||||
|
for i, someFloat := range floats {
|
||||||
|
pgtypeFloat8Array[i].Float = someFloat
|
||||||
|
pgtypeFloat8Array[i].Status = pgtype.Present
|
||||||
|
}
|
||||||
|
return &pgtype.Float8Array{
|
||||||
|
Elements: pgtypeFloat8Array,
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(floats)), LowerBound: 1}},
|
||||||
|
Status: pgtype.Present,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeIDArray returns an object usable by pg drivers for passing a []storj.NodeID
|
||||||
|
// slice into a database as type BYTEA[].
|
||||||
|
func NodeIDArray(nodeIDs []storj.NodeID) *pgtype.ByteaArray {
|
||||||
|
if nodeIDs == nil {
|
||||||
|
return &pgtype.ByteaArray{Status: pgtype.Null}
|
||||||
|
}
|
||||||
|
pgtypeByteaArray := make([]pgtype.Bytea, len(nodeIDs))
|
||||||
|
for i, nodeID := range nodeIDs {
|
||||||
|
nodeIDCopy := nodeID
|
||||||
|
pgtypeByteaArray[i].Bytes = nodeIDCopy[:]
|
||||||
|
pgtypeByteaArray[i].Status = pgtype.Present
|
||||||
|
}
|
||||||
|
return &pgtype.ByteaArray{
|
||||||
|
Elements: pgtypeByteaArray,
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(nodeIDs)), LowerBound: 1}},
|
||||||
|
Status: pgtype.Present,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user