mirror of
https://github.com/absmach/supermq.git
synced 2026-08-07 11:44:41 +00:00
MF-1667 - Postgres lib update (#1680)
* Replace SQL lib in Things and Users Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Update Bootstrap DB lib Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Rename pqErr to pgErr Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Update Auth db lib Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Update DB lib in consumers Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Update DB lib for Readers Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Update Certs DB lib Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Remove "lib/pq" from dependencies Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Update vendor Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Update Go version for base image in Dockerfile Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Remove unused Provision Dockerfile Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Move error codes to init.go files Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add missing comment Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Use Error Codes lib Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Update Go version in CI script Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Update mod file Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
This commit is contained in:
+35
-33
@@ -12,9 +12,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
)
|
||||
@@ -23,10 +24,7 @@ var (
|
||||
errStringToUUID = errors.New("error converting string to uuid")
|
||||
errGetTotal = errors.New("failed to get total number of groups")
|
||||
errCreateMetadataQuery = errors.New("failed to create query for metadata")
|
||||
|
||||
errTruncation = "string_data_right_truncation"
|
||||
errFK = "foreign_key_violation"
|
||||
groupIDFkeyy = "group_relations_group_id_fkey"
|
||||
groupIDFkeyy = "group_relations_group_id_fkey"
|
||||
)
|
||||
|
||||
var _ auth.GroupRepository = (*groupRepository)(nil)
|
||||
@@ -62,19 +60,21 @@ func (gr groupRepository) Save(ctx context.Context, g auth.Group) (auth.Group, e
|
||||
|
||||
row, err := gr.db.NamedQueryContext(ctx, q, dbg)
|
||||
if err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid, errTruncation:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return auth.Group{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
case errFK:
|
||||
case pgerrcode.ForeignKeyViolation:
|
||||
return auth.Group{}, errors.Wrap(errors.ErrCreateEntity, err)
|
||||
case errDuplicate:
|
||||
case pgerrcode.UniqueViolation:
|
||||
return auth.Group{}, errors.Wrap(errors.ErrConflict, err)
|
||||
case pgerrcode.StringDataRightTruncationDataException:
|
||||
return auth.Group{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
}
|
||||
|
||||
return auth.Group{}, errors.Wrap(errors.ErrCreateEntity, errors.New(pqErr.Message))
|
||||
return auth.Group{}, errors.Wrap(errors.ErrCreateEntity, err)
|
||||
}
|
||||
|
||||
defer row.Close()
|
||||
@@ -98,16 +98,18 @@ func (gr groupRepository) Update(ctx context.Context, g auth.Group) (auth.Group,
|
||||
|
||||
row, err := gr.db.NamedQueryContext(ctx, q, dbu)
|
||||
if err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid, errTruncation:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return auth.Group{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
case errDuplicate:
|
||||
case pgerrcode.UniqueViolation:
|
||||
return auth.Group{}, errors.Wrap(errors.ErrConflict, err)
|
||||
case pgerrcode.StringDataRightTruncationDataException:
|
||||
return auth.Group{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
}
|
||||
return auth.Group{}, errors.Wrap(errors.ErrUpdateEntity, errors.New(pqErr.Message))
|
||||
return auth.Group{}, errors.Wrap(errors.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
defer row.Close()
|
||||
@@ -132,20 +134,20 @@ func (gr groupRepository) Delete(ctx context.Context, groupID string) error {
|
||||
|
||||
res, err := gr.db.NamedExecContext(ctx, qd, dbg)
|
||||
if err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pqErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid, errTruncation:
|
||||
switch pqErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
case errFK:
|
||||
switch pqErr.Constraint {
|
||||
case pgerrcode.ForeignKeyViolation:
|
||||
switch pqErr.ConstraintName {
|
||||
case groupIDFkeyy:
|
||||
return errors.Wrap(auth.ErrGroupNotEmpty, err)
|
||||
}
|
||||
return errors.Wrap(errors.ErrConflict, err)
|
||||
}
|
||||
}
|
||||
return errors.Wrap(errors.ErrUpdateEntity, errors.New(pqErr.Message))
|
||||
return errors.Wrap(errors.ErrUpdateEntity, err)
|
||||
}
|
||||
|
||||
cnt, err := res.RowsAffected()
|
||||
@@ -439,15 +441,15 @@ func (gr groupRepository) Assign(ctx context.Context, groupID, groupType string,
|
||||
|
||||
if _, err := tx.NamedExecContext(ctx, qIns, dbg); err != nil {
|
||||
tx.Rollback()
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid, errTruncation:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
case errFK:
|
||||
return errors.Wrap(errors.ErrConflict, errors.New(pqErr.Detail))
|
||||
case errDuplicate:
|
||||
return errors.Wrap(auth.ErrMemberAlreadyAssigned, errors.New(pqErr.Detail))
|
||||
case pgerrcode.ForeignKeyViolation:
|
||||
return errors.Wrap(errors.ErrConflict, errors.New(pgErr.Detail))
|
||||
case pgerrcode.UniqueViolation:
|
||||
return errors.Wrap(auth.ErrMemberAlreadyAssigned, errors.New(pgErr.Detail))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,12 +480,12 @@ func (gr groupRepository) Unassign(ctx context.Context, groupID string, ids ...s
|
||||
|
||||
if _, err := tx.NamedExecContext(ctx, qDel, dbg); err != nil {
|
||||
tx.Rollback()
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid, errTruncation:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
case errDuplicate:
|
||||
case pgerrcode.UniqueViolation:
|
||||
return errors.Wrap(errors.ErrConflict, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package postgres
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
)
|
||||
@@ -28,7 +29,7 @@ type Config struct {
|
||||
func Connect(cfg Config) (*sqlx.DB, error) {
|
||||
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
|
||||
|
||||
db, err := sqlx.Open("postgres", url)
|
||||
db, err := sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+6
-13
@@ -5,7 +5,8 @@ import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
)
|
||||
@@ -17,11 +18,6 @@ var (
|
||||
)
|
||||
var _ auth.KeyRepository = (*repo)(nil)
|
||||
|
||||
const (
|
||||
errDuplicate = "unique_violation"
|
||||
errInvalid = "invalid_text_representation"
|
||||
)
|
||||
|
||||
type repo struct {
|
||||
db Database
|
||||
}
|
||||
@@ -40,11 +36,8 @@ func (kr repo) Save(ctx context.Context, key auth.Key) (string, error) {
|
||||
dbKey := toDBKey(key)
|
||||
if _, err := kr.db.NamedExecContext(ctx, q, dbKey); err != nil {
|
||||
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
if ok {
|
||||
if pqErr.Code.Name() == errDuplicate {
|
||||
return "", errors.Wrap(errors.ErrConflict, pqErr)
|
||||
}
|
||||
if pgErr, ok := err.(*pgconn.PgError); ok && pgErr.Code == pgerrcode.UniqueViolation {
|
||||
return "", errors.Wrap(errors.ErrConflict, err)
|
||||
}
|
||||
|
||||
return "", errors.Wrap(errSave, err)
|
||||
@@ -57,8 +50,8 @@ func (kr repo) Retrieve(ctx context.Context, issuerID, id string) (auth.Key, err
|
||||
q := `SELECT id, type, issuer_id, subject, issued_at, expires_at FROM keys WHERE issuer_id = $1 AND id = $2`
|
||||
key := dbKey{}
|
||||
if err := kr.db.QueryRowxContext(ctx, q, issuerID, id).StructScan(&key); err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
if err == sql.ErrNoRows || ok && errInvalid == pqErr.Code.Name() {
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if err == sql.ErrNoRows || ok && pgerrcode.InvalidTextRepresentation == pgErr.Code {
|
||||
return auth.Key{}, errors.Wrap(errors.ErrNotFound, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/mainflux/mainflux/auth/postgres"
|
||||
dockertest "github.com/ory/dockertest/v3"
|
||||
@@ -39,7 +40,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
if err := pool.Retry(func() error {
|
||||
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
|
||||
db, err := sql.Open("postgres", url)
|
||||
db, err := sql.Open("pgx", url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,21 +9,15 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq"
|
||||
"github.com/mainflux/mainflux/bootstrap"
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
duplicateErr = "unique_violation"
|
||||
fkViolation = "foreign_key_violation"
|
||||
connConstraintErr = "connections_config_id_config_owner_fkey"
|
||||
cleanupQuery = `DELETE FROM channels ch WHERE NOT EXISTS (
|
||||
SELECT channel_id FROM connections c WHERE ch.mainflux_channel = c.channel_id);`
|
||||
)
|
||||
|
||||
var (
|
||||
errSaveChannels = errors.New("failed to insert channels to database")
|
||||
errSaveConnections = errors.New("failed to insert connections to database")
|
||||
@@ -32,6 +26,9 @@ var (
|
||||
errDisconnectThing = errors.New("failed to disconnect thing in bootstrap configuration in database")
|
||||
)
|
||||
|
||||
const cleanupQuery = `DELETE FROM channels ch WHERE NOT EXISTS (
|
||||
SELECT channel_id FROM connections c WHERE ch.mainflux_channel = c.channel_id);`
|
||||
|
||||
var _ bootstrap.ConfigRepository = (*configRepository)(nil)
|
||||
|
||||
type configRepository struct {
|
||||
@@ -58,7 +55,7 @@ func (cr configRepository) Save(cfg bootstrap.Config, chsConnIDs []string) (stri
|
||||
|
||||
if _, err := tx.NamedExec(q, dbcfg); err != nil {
|
||||
e := err
|
||||
if pqErr, ok := err.(*pq.Error); ok && pqErr.Code.Name() == duplicateErr {
|
||||
if pgErr, ok := err.(*pgconn.PgError); ok && pgErr.Code == pgerrcode.UniqueViolation {
|
||||
e = errors.ErrConflict
|
||||
}
|
||||
|
||||
@@ -289,8 +286,8 @@ func (cr configRepository) UpdateConnections(owner, id string, channels []bootst
|
||||
}
|
||||
|
||||
if err := updateConnections(owner, id, connections, tx); err != nil {
|
||||
if e, ok := err.(*pq.Error); ok {
|
||||
if e.Code.Name() == fkViolation && e.Constraint == connConstraintErr {
|
||||
if e, ok := err.(*pgconn.PgError); ok {
|
||||
if e.Code == pgerrcode.ForeignKeyViolation {
|
||||
return errors.ErrNotFound
|
||||
}
|
||||
}
|
||||
@@ -345,8 +342,13 @@ func (cr configRepository) ListExisting(owner string, ids []string) ([]bootstrap
|
||||
return channels, nil
|
||||
}
|
||||
|
||||
var chans pgtype.TextArray
|
||||
if err := chans.Set(ids); err != nil {
|
||||
return []bootstrap.Channel{}, err
|
||||
}
|
||||
|
||||
q := "SELECT mainflux_channel, name, metadata FROM channels WHERE owner = $1 AND mainflux_channel = ANY ($2)"
|
||||
rows, err := cr.db.Queryx(q, owner, pq.Array(ids))
|
||||
rows, err := cr.db.Queryx(q, owner, chans)
|
||||
if err != nil {
|
||||
return []bootstrap.Channel{}, errors.Wrap(errors.ErrViewEntity, err)
|
||||
}
|
||||
@@ -460,7 +462,7 @@ func insertChannels(owner string, channels []bootstrap.Channel, tx *sqlx.Tx) err
|
||||
VALUES (:mainflux_channel, :owner, :name, :metadata)`
|
||||
if _, err := tx.NamedExec(q, chans); err != nil {
|
||||
e := err
|
||||
if pqErr, ok := err.(*pq.Error); ok && pqErr.Code.Name() == duplicateErr {
|
||||
if pqErr, ok := err.(*pgconn.PgError); ok && pqErr.Code == pgerrcode.UniqueViolation {
|
||||
e = errors.ErrConflict
|
||||
}
|
||||
return e
|
||||
@@ -500,7 +502,12 @@ func updateConnections(owner, id string, connections []string, tx *sqlx.Tx) erro
|
||||
WHERE config_id = $1 AND config_owner = $2 AND channel_owner = $2
|
||||
AND channel_id NOT IN ($3)`
|
||||
|
||||
res, err := tx.Exec(q, id, owner, pq.Array(connections))
|
||||
var conn pgtype.TextArray
|
||||
if err := conn.Set(connections); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := tx.Exec(q, id, owner, conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ package postgres
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq" // required for SQL access
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ type Config struct {
|
||||
func Connect(cfg Config) (*sqlx.DB, error) {
|
||||
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
|
||||
|
||||
db, err := sqlx.Open("postgres", url)
|
||||
db, err := sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/mainflux/mainflux/bootstrap/postgres"
|
||||
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
dockertest "github.com/ory/dockertest/v3"
|
||||
)
|
||||
@@ -39,7 +41,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
if err := pool.Retry(func() error {
|
||||
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
|
||||
db, err = sqlx.Open("postgres", url)
|
||||
db, err = sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,19 +9,18 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq"
|
||||
"github.com/mainflux/mainflux/certs"
|
||||
"github.com/mainflux/mainflux/logger"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
)
|
||||
|
||||
const duplicateErr = "unique_violation"
|
||||
|
||||
var errInvalid = "invalid_text_representation"
|
||||
|
||||
var _ certs.Repository = (*certsRepository)(nil)
|
||||
|
||||
// Cert holds info on expiration date for specific cert issued for specific Thing.
|
||||
type Cert struct {
|
||||
ThingID string
|
||||
Serial string
|
||||
@@ -86,7 +85,7 @@ func (cr certsRepository) Save(ctx context.Context, cert certs.Cert) (string, er
|
||||
|
||||
if _, err := tx.NamedExec(q, dbcrt); err != nil {
|
||||
e := err
|
||||
if pqErr, ok := err.(*pq.Error); ok && pqErr.Code.Name() == duplicateErr {
|
||||
if pgErr, ok := err.(*pgconn.PgError); ok && pgErr.Code == pgerrcode.UniqueViolation {
|
||||
e = errors.New("error conflict")
|
||||
}
|
||||
|
||||
@@ -158,8 +157,8 @@ func (cr certsRepository) RetrieveBySerial(ctx context.Context, ownerID, serialI
|
||||
|
||||
if err := cr.db.QueryRowxContext(ctx, q, ownerID, serialID).StructScan(&dbcrt); err != nil {
|
||||
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
if err == sql.ErrNoRows || ok && errInvalid == pqErr.Code.Name() {
|
||||
pqErr, ok := err.(*pgconn.PgError)
|
||||
if err == sql.ErrNoRows || ok && pgerrcode.InvalidTextRepresentation == pqErr.Code {
|
||||
return c, errors.Wrap(errors.ErrNotFound, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
)
|
||||
@@ -37,7 +38,7 @@ type Config struct {
|
||||
func Connect(cfg Config) (*sqlx.DB, error) {
|
||||
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
|
||||
|
||||
db, err := sqlx.Open("postgres", url)
|
||||
db, err := sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
@@ -36,11 +34,7 @@ func NewDatabase(db *sqlx.DB) Database {
|
||||
|
||||
func (dm database) NamedExecContext(ctx context.Context, query string, args interface{}) (sql.Result, error) {
|
||||
addSpanTags(ctx, query)
|
||||
result, err := dm.db.NamedExecContext(ctx, query, args)
|
||||
if pqErr, ok := err.(*pq.Error); ok && errDuplicate == pqErr.Code.Name() {
|
||||
return result, errors.Wrap(errors.ErrConflict, err)
|
||||
}
|
||||
return result, err
|
||||
return dm.db.NamedExecContext(ctx, query, args)
|
||||
}
|
||||
|
||||
func (dm database) QueryRowxContext(ctx context.Context, query string, args ...interface{}) *sqlx.Row {
|
||||
|
||||
@@ -6,8 +6,8 @@ package postgres
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq" // required for SQL access
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ type Config struct {
|
||||
func Connect(cfg Config) (*sqlx.DB, error) {
|
||||
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
|
||||
|
||||
db, err := sqlx.Open("postgres", url)
|
||||
db, err := sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/mainflux/mainflux/consumers/notifiers/postgres"
|
||||
"github.com/mainflux/mainflux/pkg/ulid"
|
||||
@@ -42,7 +43,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
|
||||
if err := pool.Retry(func() error {
|
||||
db, err = sqlx.Open("postgres", url)
|
||||
db, err = sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,15 +9,14 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
notifiers "github.com/mainflux/mainflux/consumers/notifiers"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
)
|
||||
|
||||
var _ notifiers.SubscriptionsRepository = (*subscriptionsRepo)(nil)
|
||||
|
||||
const errDuplicate = "unique_violation"
|
||||
|
||||
type subscriptionsRepo struct {
|
||||
db Database
|
||||
}
|
||||
@@ -41,7 +40,7 @@ func (repo subscriptionsRepo) Save(ctx context.Context, sub notifiers.Subscripti
|
||||
|
||||
row, err := repo.db.NamedQueryContext(ctx, q, dbSub)
|
||||
if err != nil {
|
||||
if pqErr, ok := err.(*pq.Error); ok && pqErr.Code.Name() == errDuplicate {
|
||||
if pqErr, ok := err.(*pgconn.PgError); ok && pqErr.Code == pgerrcode.UniqueViolation {
|
||||
return "", errors.Wrap(errors.ErrConflict, err)
|
||||
}
|
||||
return "", errors.Wrap(errors.ErrCreateEntity, err)
|
||||
|
||||
@@ -9,19 +9,15 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq" // required for DB access
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jmoiron/sqlx" // required for DB access
|
||||
"github.com/mainflux/mainflux/consumers"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
mfjson "github.com/mainflux/mainflux/pkg/transformers/json"
|
||||
"github.com/mainflux/mainflux/pkg/transformers/senml"
|
||||
)
|
||||
|
||||
const (
|
||||
errInvalid = "invalid_text_representation"
|
||||
errUndefinedTable = "undefined_table"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidMessage = errors.New("invalid message representation")
|
||||
errSaveMessage = errors.New("failed to save message to postgres database")
|
||||
@@ -85,10 +81,10 @@ func (pr postgresRepo) saveSenml(messages interface{}) (err error) {
|
||||
}
|
||||
m := senmlMessage{Message: msg, ID: id.String()}
|
||||
if _, err := tx.NamedExec(q, m); err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return errors.Wrap(errSaveMessage, errInvalidMessage)
|
||||
}
|
||||
}
|
||||
@@ -140,13 +136,14 @@ func (pr postgresRepo) insertJSON(msgs mfjson.Messages) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(errSaveMessage, err)
|
||||
}
|
||||
|
||||
if _, err = tx.NamedExec(q, dbmsg); err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return errors.Wrap(errSaveMessage, errInvalidMessage)
|
||||
case errUndefinedTable:
|
||||
case pgerrcode.UndefinedTable:
|
||||
return errNoTable
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ package postgres
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq" // required for SQL access
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ type Config struct {
|
||||
func Connect(cfg Config) (*sqlx.DB, error) {
|
||||
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
|
||||
|
||||
db, err := sqlx.Open("postgres", url)
|
||||
db, err := sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/mainflux/mainflux/consumers/writers/postgres"
|
||||
dockertest "github.com/ory/dockertest/v3"
|
||||
@@ -38,7 +39,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
if err := pool.Retry(func() error {
|
||||
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
|
||||
db, err = sqlx.Open("postgres", url)
|
||||
db, err = sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,19 +8,15 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq" // required for DB access
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jmoiron/sqlx" // required for DB access
|
||||
"github.com/mainflux/mainflux/consumers"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
mfjson "github.com/mainflux/mainflux/pkg/transformers/json"
|
||||
"github.com/mainflux/mainflux/pkg/transformers/senml"
|
||||
)
|
||||
|
||||
const (
|
||||
errInvalid = "invalid_text_representation"
|
||||
errUndefinedTable = "undefined_table"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidMessage = errors.New("invalid message representation")
|
||||
errSaveMessage = errors.New("failed to save message to timescale database")
|
||||
@@ -80,10 +76,10 @@ func (tr timescaleRepo) saveSenml(messages interface{}) (err error) {
|
||||
for _, msg := range msgs {
|
||||
m := senmlMessage{Message: msg}
|
||||
if _, err := tx.NamedExec(q, m); err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return errors.Wrap(errSaveMessage, errInvalidMessage)
|
||||
}
|
||||
}
|
||||
@@ -136,12 +132,12 @@ func (tr timescaleRepo) insertJSON(msgs mfjson.Messages) error {
|
||||
return errors.Wrap(errSaveMessage, err)
|
||||
}
|
||||
if _, err = tx.NamedExec(q, dbmsg); err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return errors.Wrap(errSaveMessage, errInvalidMessage)
|
||||
case errUndefinedTable:
|
||||
case pgerrcode.UndefinedTable:
|
||||
return errNoTable
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ package timescale
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq" // required for SQL access
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ type Config struct {
|
||||
func Connect(cfg Config) (*sqlx.DB, error) {
|
||||
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
|
||||
|
||||
db, err := sqlx.Open("postgres", url)
|
||||
db, err := sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
"github.com/mainflux/mainflux/consumers/writers/timescale"
|
||||
dockertest "github.com/ory/dockertest/v3"
|
||||
)
|
||||
@@ -38,7 +40,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
if err := pool.Retry(func() error {
|
||||
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
|
||||
db, err = sqlx.Open("postgres", url)
|
||||
db, err = sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.17-alpine AS builder
|
||||
FROM golang:1.19-alpine AS builder
|
||||
ARG SVC
|
||||
ARG GOARCH
|
||||
ARG GOARM
|
||||
|
||||
@@ -21,8 +21,10 @@ require (
|
||||
github.com/hashicorp/vault/api v1.8.1
|
||||
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f
|
||||
github.com/influxdata/influxdb v1.10.0
|
||||
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa
|
||||
github.com/jackc/pgtype v1.13.0
|
||||
github.com/jackc/pgx/v5 v5.2.0
|
||||
github.com/jmoiron/sqlx v1.3.5
|
||||
github.com/lib/pq v1.10.7
|
||||
github.com/mainflux/mproxy v0.2.3
|
||||
github.com/mainflux/senml v1.5.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
@@ -94,6 +96,9 @@ require (
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/klauspost/compress v1.15.11 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
|
||||
@@ -97,14 +97,18 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
|
||||
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
@@ -196,6 +200,7 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
|
||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE=
|
||||
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc=
|
||||
github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
@@ -364,6 +369,54 @@ github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7P
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/influxdata/influxdb v1.10.0 h1:8xDpt8KO3lzrzf/ss+l8r42AGUZvoITu5824berK7SE=
|
||||
github.com/influxdata/influxdb v1.10.0/go.mod h1:IVPuoA2pOOxau/NguX7ipW0Jp9Bn+dMWlo0+VOscevU=
|
||||
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530 h1:dUJ578zuPEsXjtzOfEF0q9zDAfljJ9oFnTHcQaNkccw=
|
||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw=
|
||||
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
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/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||
github.com/jackc/pgtype v1.13.0 h1:XkIc7A+1BmZD19bB2NxrtjJweHxQ9agqvM+9URc68Cg=
|
||||
github.com/jackc/pgtype v1.13.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c h1:Dznn52SgVIVst9UyOT9brctYUgxs+CvVfPaC3jKrA50=
|
||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||
github.com/jackc/pgx/v5 v5.2.0 h1:NdPpngX0Y6z6XDFKqmFQaE+bCtkqzvQIOt1wvBlAqs8=
|
||||
github.com/jackc/pgx/v5 v5.2.0/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
|
||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||
@@ -391,6 +444,7 @@ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e
|
||||
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
|
||||
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
@@ -399,15 +453,18 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/lestrrat-go/iter v0.0.0-20200422075355-fc1769541911/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
|
||||
github.com/lestrrat-go/jwx v1.0.2/go.mod h1:TPF17WiSFegZo+c20fdpw49QD+/7n4/IsGvEmCSWwT0=
|
||||
github.com/lestrrat-go/pdebug v0.0.0-20200204225717-4d6bd78da58d/go.mod h1:B06CSso/AWxiPejj+fheUINGeBKeeEZNt8w+EoU7+L8=
|
||||
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/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
@@ -422,11 +479,15 @@ github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2
|
||||
github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=
|
||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
@@ -591,6 +652,9 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||
github.com/rubenv/sql-migrate v1.2.0 h1:fOXMPLMd41sK7Tg75SXDec15k3zg5WNV6SjuDRiNfcU=
|
||||
github.com/rubenv/sql-migrate v1.2.0/go.mod h1:Z5uVnq7vrIrPmHbVFfR4YLHRZquxeHpckCnRq0P/K9Y=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@@ -599,11 +663,14 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
@@ -630,6 +697,7 @@ github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU=
|
||||
github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
@@ -688,6 +756,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
@@ -701,7 +770,9 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
@@ -709,16 +780,20 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0
|
||||
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
||||
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -729,8 +804,11 @@ golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
@@ -790,6 +868,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -860,7 +939,9 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -870,6 +951,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -930,6 +1012,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
|
||||
@@ -960,6 +1043,7 @@ golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
@@ -967,6 +1051,7 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -980,6 +1065,7 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@@ -1013,6 +1099,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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=
|
||||
@@ -1143,6 +1231,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
FROM golang:1.13-alpine AS builder
|
||||
ARG SVC
|
||||
ARG GOARCH
|
||||
ARG GOARM
|
||||
|
||||
WORKDIR /go/src/github.com/mainflux/license
|
||||
COPY . .
|
||||
RUN apk update \
|
||||
&& apk add make\
|
||||
&& make $SVC \
|
||||
&& mv build/mainflux-license-$SVC /exe
|
||||
|
||||
FROM scratch
|
||||
# Certificates are needed so that mailing util can work.
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
COPY --from=builder /exe /
|
||||
ENTRYPOINT ["/exe"]
|
||||
@@ -6,11 +6,14 @@ package postgres
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq" // required for SQL access
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
)
|
||||
|
||||
// Table for SenML messages
|
||||
const defTable = "messages"
|
||||
|
||||
// Config defines the options that are used when connecting to a PostgreSQL instance
|
||||
type Config struct {
|
||||
Host string
|
||||
@@ -30,7 +33,7 @@ type Config struct {
|
||||
func Connect(cfg Config) (*sqlx.DB, error) {
|
||||
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
|
||||
|
||||
db, err := sqlx.Open("postgres", url)
|
||||
db, err := sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -7,21 +7,14 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jmoiron/sqlx" // required for DB access
|
||||
"github.com/lib/pq"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/pkg/transformers/senml"
|
||||
"github.com/mainflux/mainflux/readers"
|
||||
)
|
||||
|
||||
const (
|
||||
// Table for SenML messages
|
||||
defTable = "messages"
|
||||
|
||||
// Error code for Undefined table error.
|
||||
undefinedTableCode = "42P01"
|
||||
)
|
||||
|
||||
var _ readers.MessageRepository = (*postgresRepository)(nil)
|
||||
|
||||
type postgresRepository struct {
|
||||
@@ -66,8 +59,8 @@ func (tr postgresRepository) ReadAll(chanID string, rpm readers.PageMetadata) (r
|
||||
|
||||
rows, err := tr.db.NamedQuery(q, params)
|
||||
if err != nil {
|
||||
if e, ok := err.(*pq.Error); ok {
|
||||
if e.Code == undefinedTableCode {
|
||||
if pgErr, ok := err.(*pgconn.PgError); ok {
|
||||
if pgErr.Code == pgerrcode.UndefinedTable {
|
||||
return readers.MessagesPage{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/mainflux/mainflux/pkg/uuid"
|
||||
"github.com/mainflux/mainflux/readers"
|
||||
preader "github.com/mainflux/mainflux/readers/postgres"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/mainflux/mainflux/readers/postgres"
|
||||
dockertest "github.com/ory/dockertest/v3"
|
||||
@@ -38,7 +39,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
if err = pool.Retry(func() error {
|
||||
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
|
||||
db, err = sqlx.Open("postgres", url)
|
||||
db, err = sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,11 +6,14 @@ package timescale
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq" // required for SQL access
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
)
|
||||
|
||||
// Table for SenML messages
|
||||
const defTable = "messages"
|
||||
|
||||
// Config defines the options that are used when connecting to a TimescaleSQL instance
|
||||
type Config struct {
|
||||
Host string
|
||||
@@ -30,7 +33,7 @@ type Config struct {
|
||||
func Connect(cfg Config) (*sqlx.DB, error) {
|
||||
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
|
||||
|
||||
db, err := sqlx.Open("postgres", url)
|
||||
db, err := sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -7,21 +7,14 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jmoiron/sqlx" // required for DB access
|
||||
"github.com/lib/pq"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/pkg/transformers/senml"
|
||||
"github.com/mainflux/mainflux/readers"
|
||||
)
|
||||
|
||||
const (
|
||||
// Table for SenML messages
|
||||
defTable = "messages"
|
||||
|
||||
// Error code for Undefined table error.
|
||||
undefinedTableCode = "42P01"
|
||||
)
|
||||
|
||||
var _ readers.MessageRepository = (*timescaleRepository)(nil)
|
||||
|
||||
type timescaleRepository struct {
|
||||
@@ -64,8 +57,8 @@ func (tr timescaleRepository) ReadAll(chanID string, rpm readers.PageMetadata) (
|
||||
|
||||
rows, err := tr.db.NamedQuery(q, params)
|
||||
if err != nil {
|
||||
if e, ok := err.(*pq.Error); ok {
|
||||
if e.Code == undefinedTableCode {
|
||||
if pgErr, ok := err.(*pgconn.PgError); ok {
|
||||
if pgErr.Code == pgerrcode.UndefinedTable {
|
||||
return readers.MessagesPage{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/mainflux/mainflux/readers/timescale"
|
||||
dockertest "github.com/ory/dockertest/v3"
|
||||
@@ -38,7 +39,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
if err = pool.Retry(func() error {
|
||||
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
|
||||
db, err = sqlx.Open("postgres", url)
|
||||
db, err = sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# This script contains commands to be executed by the CI tool.
|
||||
NPROC=$(nproc)
|
||||
GO_VERSION=1.18.3
|
||||
GO_VERSION=1.19.4
|
||||
PROTOC_VERSION=3.12.3
|
||||
PROTOC_GEN_VERSION=v1.4.2
|
||||
PROTOC_GOFAST_VERSION=v1.3.1
|
||||
|
||||
+28
-21
@@ -12,7 +12,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/lib/pq"
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/things"
|
||||
)
|
||||
@@ -52,13 +53,15 @@ func (cr channelRepository) Save(ctx context.Context, channels ...things.Channel
|
||||
_, err = tx.NamedExecContext(ctx, q, dbch)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid, errTruncation:
|
||||
return []things.Channel{}, errors.ErrMalformedEntity
|
||||
case errDuplicate:
|
||||
return []things.Channel{}, errors.ErrConflict
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return []things.Channel{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
case pgerrcode.UniqueViolation:
|
||||
return []things.Channel{}, errors.Wrap(errors.ErrConflict, err)
|
||||
case pgerrcode.StringDataRightTruncationDataException:
|
||||
return []things.Channel{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
}
|
||||
return []things.Channel{}, errors.Wrap(errors.ErrCreateEntity, err)
|
||||
@@ -79,11 +82,13 @@ func (cr channelRepository) Update(ctx context.Context, channel things.Channel)
|
||||
|
||||
res, err := cr.db.NamedExecContext(ctx, q, dbch)
|
||||
if err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid, errTruncation:
|
||||
return errors.ErrMalformedEntity
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
case pgerrcode.StringDataRightTruncationDataException:
|
||||
return errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,9 +113,11 @@ func (cr channelRepository) RetrieveByID(ctx context.Context, owner, id string)
|
||||
dbch := dbChannel{
|
||||
ID: id,
|
||||
}
|
||||
|
||||
if err := cr.db.QueryRowxContext(ctx, q, id).StructScan(&dbch); err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
if err == sql.ErrNoRows || ok && errInvalid == pqErr.Code.Name() {
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
// If there is no result or ID is in an invalid format, return ErrNotFound.
|
||||
if err == sql.ErrNoRows || ok && pgerrcode.InvalidTextRepresentation == pgErr.Code {
|
||||
return things.Channel{}, errors.ErrNotFound
|
||||
}
|
||||
return things.Channel{}, errors.Wrap(errors.ErrViewEntity, err)
|
||||
@@ -308,12 +315,12 @@ func (cr channelRepository) Connect(ctx context.Context, owner string, chIDs, th
|
||||
_, err := tx.NamedExecContext(ctx, q, dbco)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errFK:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.ForeignKeyViolation:
|
||||
return errors.ErrNotFound
|
||||
case errDuplicate:
|
||||
case pgerrcode.UniqueViolation:
|
||||
return errors.ErrConflict
|
||||
}
|
||||
}
|
||||
@@ -351,12 +358,12 @@ func (cr channelRepository) Disconnect(ctx context.Context, owner string, chIDs,
|
||||
res, err := tx.NamedExecContext(ctx, q, dbco)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errFK:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.ForeignKeyViolation:
|
||||
return errors.ErrNotFound
|
||||
case errDuplicate:
|
||||
case pgerrcode.UniqueViolation:
|
||||
return errors.ErrConflict
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ package postgres
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq" // required for SQL access
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ type Config struct {
|
||||
func Connect(cfg Config) (*sqlx.DB, error) {
|
||||
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
|
||||
|
||||
db, err := sqlx.Open("postgres", url)
|
||||
db, err := sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/mainflux/mainflux/things/postgres"
|
||||
dockertest "github.com/ory/dockertest/v3"
|
||||
@@ -44,7 +45,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
if err := pool.Retry(func() error {
|
||||
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
|
||||
db, err = sqlx.Open("postgres", url)
|
||||
db, err = sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+23
-21
@@ -11,18 +11,13 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/lib/pq" // required for DB access
|
||||
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/things"
|
||||
)
|
||||
|
||||
const (
|
||||
errDuplicate = "unique_violation"
|
||||
errFK = "foreign_key_violation"
|
||||
errInvalid = "invalid_text_representation"
|
||||
errTruncation = "string_data_right_truncation"
|
||||
)
|
||||
|
||||
var _ things.ThingRepository = (*thingRepository)(nil)
|
||||
|
||||
type thingRepository struct {
|
||||
@@ -54,13 +49,15 @@ func (tr thingRepository) Save(ctx context.Context, ths ...things.Thing) ([]thin
|
||||
|
||||
if _, err := tx.NamedExecContext(ctx, q, dbth); err != nil {
|
||||
tx.Rollback()
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid, errTruncation:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return []things.Thing{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
case errDuplicate:
|
||||
case pgerrcode.UniqueViolation:
|
||||
return []things.Thing{}, errors.Wrap(errors.ErrConflict, err)
|
||||
case pgerrcode.StringDataRightTruncationDataException:
|
||||
return []things.Thing{}, errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,11 +82,13 @@ func (tr thingRepository) Update(ctx context.Context, t things.Thing) error {
|
||||
|
||||
res, errdb := tr.db.NamedExecContext(ctx, q, dbth)
|
||||
if errdb != nil {
|
||||
pqErr, ok := errdb.(*pq.Error)
|
||||
pgErr, ok := errdb.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid, errTruncation:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return errors.Wrap(errors.ErrMalformedEntity, errdb)
|
||||
case pgerrcode.StringDataRightTruncationDataException:
|
||||
return errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,13 +118,15 @@ func (tr thingRepository) UpdateKey(ctx context.Context, owner, id, key string)
|
||||
|
||||
res, err := tr.db.NamedExecContext(ctx, q, dbth)
|
||||
if err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
case errDuplicate:
|
||||
case pgerrcode.UniqueViolation:
|
||||
return errors.Wrap(errors.ErrConflict, err)
|
||||
case pgerrcode.StringDataRightTruncationDataException:
|
||||
return errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,8 +151,9 @@ func (tr thingRepository) RetrieveByID(ctx context.Context, owner, id string) (t
|
||||
dbth := dbThing{ID: id}
|
||||
|
||||
if err := tr.db.QueryRowxContext(ctx, q, id).StructScan(&dbth); err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
if err == sql.ErrNoRows || ok && errInvalid == pqErr.Code.Name() {
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
// If there is no result or ID is in an invalid format, return ErrNotFound.
|
||||
if err == sql.ErrNoRows || ok && pgerrcode.InvalidTextRepresentation == pgErr.Code {
|
||||
return things.Thing{}, errors.Wrap(errors.ErrNotFound, err)
|
||||
}
|
||||
return things.Thing{}, errors.Wrap(errors.ErrViewEntity, err)
|
||||
|
||||
@@ -5,8 +5,6 @@ import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
@@ -33,11 +31,7 @@ func NewDatabase(db *sqlx.DB) Database {
|
||||
|
||||
func (dm database) NamedExecContext(ctx context.Context, query string, args interface{}) (sql.Result, error) {
|
||||
addSpanTags(ctx, query)
|
||||
result, err := dm.db.NamedExecContext(ctx, query, args)
|
||||
if pqErr, ok := err.(*pq.Error); ok && errDuplicate == pqErr.Code.Name() {
|
||||
return result, errors.Wrap(errors.ErrConflict, err)
|
||||
}
|
||||
return result, err
|
||||
return dm.db.NamedExecContext(ctx, query, args)
|
||||
}
|
||||
|
||||
func (dm database) QueryRowxContext(ctx context.Context, query string, args ...interface{}) *sqlx.Row {
|
||||
|
||||
@@ -6,9 +6,9 @@ package postgres
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
_ "github.com/lib/pq" // required for SQL access
|
||||
migrate "github.com/rubenv/sql-migrate"
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ type Config struct {
|
||||
func Connect(cfg Config) (*sqlx.DB, error) {
|
||||
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
|
||||
|
||||
db, err := sqlx.Open("postgres", url)
|
||||
db, err := sqlx.Open("pgx", url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
"github.com/mainflux/mainflux/users/postgres"
|
||||
dockertest "github.com/ory/dockertest/v3"
|
||||
)
|
||||
@@ -39,7 +41,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
if err := pool.Retry(func() error {
|
||||
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
|
||||
db, err := sql.Open("postgres", url)
|
||||
db, err := sql.Open("pgx", url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+7
-12
@@ -10,21 +10,15 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/mainflux/mainflux/auth"
|
||||
"github.com/mainflux/mainflux/pkg/errors"
|
||||
"github.com/mainflux/mainflux/users"
|
||||
)
|
||||
|
||||
const (
|
||||
errInvalid = "invalid_text_representation"
|
||||
errTruncation = "string_data_right_truncation"
|
||||
)
|
||||
|
||||
var _ users.UserRepository = (*userRepository)(nil)
|
||||
|
||||
const errDuplicate = "unique_violation"
|
||||
|
||||
type userRepository struct {
|
||||
db Database
|
||||
}
|
||||
@@ -49,13 +43,14 @@ func (ur userRepository) Save(ctx context.Context, user users.User) (string, err
|
||||
}
|
||||
|
||||
row, err := ur.db.NamedQueryContext(ctx, q, dbu)
|
||||
|
||||
if err != nil {
|
||||
pqErr, ok := err.(*pq.Error)
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case errInvalid, errTruncation:
|
||||
switch pgErr.Code {
|
||||
case pgerrcode.InvalidTextRepresentation:
|
||||
return "", errors.Wrap(errors.ErrMalformedEntity, err)
|
||||
case errDuplicate:
|
||||
case pgerrcode.UniqueViolation:
|
||||
return "", errors.Wrap(errors.ErrConflict, err)
|
||||
}
|
||||
}
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
Copyright (c) 2019 Jack Christensen
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
Underlying Data Under PostgreSQL License:
|
||||
|
||||
PostgreSQL Database Management System
|
||||
(formerly known as Postgres, then as Postgres95)
|
||||
|
||||
Portions Copyright © 1996-2019, The PostgreSQL Global Development Group
|
||||
|
||||
Portions Copyright © 1994, The Regents of the University of California
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and
|
||||
without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the
|
||||
following two paragraphs appear in all copies.
|
||||
|
||||
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
|
||||
CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
|
||||
THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
|
||||
BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
|
||||
MODIFICATIONS.
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
[](https://godoc.org/github.com/jackc/pgerrcode)
|
||||
|
||||
# pgerrcode
|
||||
|
||||
Package pgerrcode contains constants for PostgreSQL error codes.
|
||||
|
||||
## License
|
||||
|
||||
MIT for this package's code and PostgreSQL License for the underlying data.
|
||||
+740
@@ -0,0 +1,740 @@
|
||||
// Package pgerrcode contains constants for PostgreSQL error codes.
|
||||
package pgerrcode
|
||||
|
||||
// Source: https://www.postgresql.org/docs/14/errcodes-appendix.html
|
||||
// See gen.rb for script that can convert the error code table to Go code.
|
||||
|
||||
const (
|
||||
|
||||
// Class 00 — Successful Completion
|
||||
SuccessfulCompletion = "00000"
|
||||
|
||||
// Class 01 — Warning
|
||||
Warning = "01000"
|
||||
DynamicResultSetsReturned = "0100C"
|
||||
ImplicitZeroBitPadding = "01008"
|
||||
NullValueEliminatedInSetFunction = "01003"
|
||||
PrivilegeNotGranted = "01007"
|
||||
PrivilegeNotRevoked = "01006"
|
||||
StringDataRightTruncationWarning = "01004"
|
||||
DeprecatedFeature = "01P01"
|
||||
|
||||
// Class 02 — No Data (this is also a warning class per the SQL standard)
|
||||
NoData = "02000"
|
||||
NoAdditionalDynamicResultSetsReturned = "02001"
|
||||
|
||||
// Class 03 — SQL Statement Not Yet Complete
|
||||
SQLStatementNotYetComplete = "03000"
|
||||
|
||||
// Class 08 — Connection Exception
|
||||
ConnectionException = "08000"
|
||||
ConnectionDoesNotExist = "08003"
|
||||
ConnectionFailure = "08006"
|
||||
SQLClientUnableToEstablishSQLConnection = "08001"
|
||||
SQLServerRejectedEstablishmentOfSQLConnection = "08004"
|
||||
TransactionResolutionUnknown = "08007"
|
||||
ProtocolViolation = "08P01"
|
||||
|
||||
// Class 09 — Triggered Action Exception
|
||||
TriggeredActionException = "09000"
|
||||
|
||||
// Class 0A — Feature Not Supported
|
||||
FeatureNotSupported = "0A000"
|
||||
|
||||
// Class 0B — Invalid Transaction Initiation
|
||||
InvalidTransactionInitiation = "0B000"
|
||||
|
||||
// Class 0F — Locator Exception
|
||||
LocatorException = "0F000"
|
||||
InvalidLocatorSpecification = "0F001"
|
||||
|
||||
// Class 0L — Invalid Grantor
|
||||
InvalidGrantor = "0L000"
|
||||
InvalidGrantOperation = "0LP01"
|
||||
|
||||
// Class 0P — Invalid Role Specification
|
||||
InvalidRoleSpecification = "0P000"
|
||||
|
||||
// Class 0Z — Diagnostics Exception
|
||||
DiagnosticsException = "0Z000"
|
||||
StackedDiagnosticsAccessedWithoutActiveHandler = "0Z002"
|
||||
|
||||
// Class 20 — Case Not Found
|
||||
CaseNotFound = "20000"
|
||||
|
||||
// Class 21 — Cardinality Violation
|
||||
CardinalityViolation = "21000"
|
||||
|
||||
// Class 22 — Data Exception
|
||||
DataException = "22000"
|
||||
ArraySubscriptError = "2202E"
|
||||
CharacterNotInRepertoire = "22021"
|
||||
DatetimeFieldOverflow = "22008"
|
||||
DivisionByZero = "22012"
|
||||
ErrorInAssignment = "22005"
|
||||
EscapeCharacterConflict = "2200B"
|
||||
IndicatorOverflow = "22022"
|
||||
IntervalFieldOverflow = "22015"
|
||||
InvalidArgumentForLogarithm = "2201E"
|
||||
InvalidArgumentForNtileFunction = "22014"
|
||||
InvalidArgumentForNthValueFunction = "22016"
|
||||
InvalidArgumentForPowerFunction = "2201F"
|
||||
InvalidArgumentForWidthBucketFunction = "2201G"
|
||||
InvalidCharacterValueForCast = "22018"
|
||||
InvalidDatetimeFormat = "22007"
|
||||
InvalidEscapeCharacter = "22019"
|
||||
InvalidEscapeOctet = "2200D"
|
||||
InvalidEscapeSequence = "22025"
|
||||
NonstandardUseOfEscapeCharacter = "22P06"
|
||||
InvalidIndicatorParameterValue = "22010"
|
||||
InvalidParameterValue = "22023"
|
||||
InvalidPrecedingOrFollowingSize = "22013"
|
||||
InvalidRegularExpression = "2201B"
|
||||
InvalidRowCountInLimitClause = "2201W"
|
||||
InvalidRowCountInResultOffsetClause = "2201X"
|
||||
InvalidTablesampleArgument = "2202H"
|
||||
InvalidTablesampleRepeat = "2202G"
|
||||
InvalidTimeZoneDisplacementValue = "22009"
|
||||
InvalidUseOfEscapeCharacter = "2200C"
|
||||
MostSpecificTypeMismatch = "2200G"
|
||||
NullValueNotAllowedDataException = "22004"
|
||||
NullValueNoIndicatorParameter = "22002"
|
||||
NumericValueOutOfRange = "22003"
|
||||
SequenceGeneratorLimitExceeded = "2200H"
|
||||
StringDataLengthMismatch = "22026"
|
||||
StringDataRightTruncationDataException = "22001"
|
||||
SubstringError = "22011"
|
||||
TrimError = "22027"
|
||||
UnterminatedCString = "22024"
|
||||
ZeroLengthCharacterString = "2200F"
|
||||
FloatingPointException = "22P01"
|
||||
InvalidTextRepresentation = "22P02"
|
||||
InvalidBinaryRepresentation = "22P03"
|
||||
BadCopyFileFormat = "22P04"
|
||||
UntranslatableCharacter = "22P05"
|
||||
NotAnXMLDocument = "2200L"
|
||||
InvalidXMLDocument = "2200M"
|
||||
InvalidXMLContent = "2200N"
|
||||
InvalidXMLComment = "2200S"
|
||||
InvalidXMLProcessingInstruction = "2200T"
|
||||
DuplicateJSONObjectKeyValue = "22030"
|
||||
InvalidArgumentForSQLJSONDatetimeFunction = "22031"
|
||||
InvalidJSONText = "22032"
|
||||
InvalidSQLJSONSubscript = "22033"
|
||||
MoreThanOneSQLJSONItem = "22034"
|
||||
NoSQLJSONItem = "22035"
|
||||
NonNumericSQLJSONItem = "22036"
|
||||
NonUniqueKeysInAJSONObject = "22037"
|
||||
SingletonSQLJSONItemRequired = "22038"
|
||||
SQLJSONArrayNotFound = "22039"
|
||||
SQLJSONMemberNotFound = "2203A"
|
||||
SQLJSONNumberNotFound = "2203B"
|
||||
SQLJSONObjectNotFound = "2203C"
|
||||
TooManyJSONArrayElements = "2203D"
|
||||
TooManyJSONObjectMembers = "2203E"
|
||||
SQLJSONScalarRequired = "2203F"
|
||||
|
||||
// Class 23 — Integrity Constraint Violation
|
||||
IntegrityConstraintViolation = "23000"
|
||||
RestrictViolation = "23001"
|
||||
NotNullViolation = "23502"
|
||||
ForeignKeyViolation = "23503"
|
||||
UniqueViolation = "23505"
|
||||
CheckViolation = "23514"
|
||||
ExclusionViolation = "23P01"
|
||||
|
||||
// Class 24 — Invalid Cursor State
|
||||
InvalidCursorState = "24000"
|
||||
|
||||
// Class 25 — Invalid Transaction State
|
||||
InvalidTransactionState = "25000"
|
||||
ActiveSQLTransaction = "25001"
|
||||
BranchTransactionAlreadyActive = "25002"
|
||||
HeldCursorRequiresSameIsolationLevel = "25008"
|
||||
InappropriateAccessModeForBranchTransaction = "25003"
|
||||
InappropriateIsolationLevelForBranchTransaction = "25004"
|
||||
NoActiveSQLTransactionForBranchTransaction = "25005"
|
||||
ReadOnlySQLTransaction = "25006"
|
||||
SchemaAndDataStatementMixingNotSupported = "25007"
|
||||
NoActiveSQLTransaction = "25P01"
|
||||
InFailedSQLTransaction = "25P02"
|
||||
IdleInTransactionSessionTimeout = "25P03"
|
||||
|
||||
// Class 26 — Invalid SQL Statement Name
|
||||
InvalidSQLStatementName = "26000"
|
||||
|
||||
// Class 27 — Triggered Data Change Violation
|
||||
TriggeredDataChangeViolation = "27000"
|
||||
|
||||
// Class 28 — Invalid Authorization Specification
|
||||
InvalidAuthorizationSpecification = "28000"
|
||||
InvalidPassword = "28P01"
|
||||
|
||||
// Class 2B — Dependent Privilege Descriptors Still Exist
|
||||
DependentPrivilegeDescriptorsStillExist = "2B000"
|
||||
DependentObjectsStillExist = "2BP01"
|
||||
|
||||
// Class 2D — Invalid Transaction Termination
|
||||
InvalidTransactionTermination = "2D000"
|
||||
|
||||
// Class 2F — SQL Routine Exception
|
||||
SQLRoutineException = "2F000"
|
||||
FunctionExecutedNoReturnStatement = "2F005"
|
||||
ModifyingSQLDataNotPermittedSQLRoutineException = "2F002"
|
||||
ProhibitedSQLStatementAttemptedSQLRoutineException = "2F003"
|
||||
ReadingSQLDataNotPermittedSQLRoutineException = "2F004"
|
||||
|
||||
// Class 34 — Invalid Cursor Name
|
||||
InvalidCursorName = "34000"
|
||||
|
||||
// Class 38 — External Routine Exception
|
||||
ExternalRoutineException = "38000"
|
||||
ContainingSQLNotPermitted = "38001"
|
||||
ModifyingSQLDataNotPermittedExternalRoutineException = "38002"
|
||||
ProhibitedSQLStatementAttemptedExternalRoutineException = "38003"
|
||||
ReadingSQLDataNotPermittedExternalRoutineException = "38004"
|
||||
|
||||
// Class 39 — External Routine Invocation Exception
|
||||
ExternalRoutineInvocationException = "39000"
|
||||
InvalidSQLstateReturned = "39001"
|
||||
NullValueNotAllowedExternalRoutineInvocationException = "39004"
|
||||
TriggerProtocolViolated = "39P01"
|
||||
SRFProtocolViolated = "39P02"
|
||||
EventTriggerProtocolViolated = "39P03"
|
||||
|
||||
// Class 3B — Savepoint Exception
|
||||
SavepointException = "3B000"
|
||||
InvalidSavepointSpecification = "3B001"
|
||||
|
||||
// Class 3D — Invalid Catalog Name
|
||||
InvalidCatalogName = "3D000"
|
||||
|
||||
// Class 3F — Invalid Schema Name
|
||||
InvalidSchemaName = "3F000"
|
||||
|
||||
// Class 40 — Transaction Rollback
|
||||
TransactionRollback = "40000"
|
||||
TransactionIntegrityConstraintViolation = "40002"
|
||||
SerializationFailure = "40001"
|
||||
StatementCompletionUnknown = "40003"
|
||||
DeadlockDetected = "40P01"
|
||||
|
||||
// Class 42 — Syntax Error or Access Rule Violation
|
||||
SyntaxErrorOrAccessRuleViolation = "42000"
|
||||
SyntaxError = "42601"
|
||||
InsufficientPrivilege = "42501"
|
||||
CannotCoerce = "42846"
|
||||
GroupingError = "42803"
|
||||
WindowingError = "42P20"
|
||||
InvalidRecursion = "42P19"
|
||||
InvalidForeignKey = "42830"
|
||||
InvalidName = "42602"
|
||||
NameTooLong = "42622"
|
||||
ReservedName = "42939"
|
||||
DatatypeMismatch = "42804"
|
||||
IndeterminateDatatype = "42P18"
|
||||
CollationMismatch = "42P21"
|
||||
IndeterminateCollation = "42P22"
|
||||
WrongObjectType = "42809"
|
||||
GeneratedAlways = "428C9"
|
||||
UndefinedColumn = "42703"
|
||||
UndefinedFunction = "42883"
|
||||
UndefinedTable = "42P01"
|
||||
UndefinedParameter = "42P02"
|
||||
UndefinedObject = "42704"
|
||||
DuplicateColumn = "42701"
|
||||
DuplicateCursor = "42P03"
|
||||
DuplicateDatabase = "42P04"
|
||||
DuplicateFunction = "42723"
|
||||
DuplicatePreparedStatement = "42P05"
|
||||
DuplicateSchema = "42P06"
|
||||
DuplicateTable = "42P07"
|
||||
DuplicateAlias = "42712"
|
||||
DuplicateObject = "42710"
|
||||
AmbiguousColumn = "42702"
|
||||
AmbiguousFunction = "42725"
|
||||
AmbiguousParameter = "42P08"
|
||||
AmbiguousAlias = "42P09"
|
||||
InvalidColumnReference = "42P10"
|
||||
InvalidColumnDefinition = "42611"
|
||||
InvalidCursorDefinition = "42P11"
|
||||
InvalidDatabaseDefinition = "42P12"
|
||||
InvalidFunctionDefinition = "42P13"
|
||||
InvalidPreparedStatementDefinition = "42P14"
|
||||
InvalidSchemaDefinition = "42P15"
|
||||
InvalidTableDefinition = "42P16"
|
||||
InvalidObjectDefinition = "42P17"
|
||||
|
||||
// Class 44 — WITH CHECK OPTION Violation
|
||||
WithCheckOptionViolation = "44000"
|
||||
|
||||
// Class 53 — Insufficient Resources
|
||||
InsufficientResources = "53000"
|
||||
DiskFull = "53100"
|
||||
OutOfMemory = "53200"
|
||||
TooManyConnections = "53300"
|
||||
ConfigurationLimitExceeded = "53400"
|
||||
|
||||
// Class 54 — Program Limit Exceeded
|
||||
ProgramLimitExceeded = "54000"
|
||||
StatementTooComplex = "54001"
|
||||
TooManyColumns = "54011"
|
||||
TooManyArguments = "54023"
|
||||
|
||||
// Class 55 — Object Not In Prerequisite State
|
||||
ObjectNotInPrerequisiteState = "55000"
|
||||
ObjectInUse = "55006"
|
||||
CantChangeRuntimeParam = "55P02"
|
||||
LockNotAvailable = "55P03"
|
||||
UnsafeNewEnumValueUsage = "55P04"
|
||||
|
||||
// Class 57 — Operator Intervention
|
||||
OperatorIntervention = "57000"
|
||||
QueryCanceled = "57014"
|
||||
AdminShutdown = "57P01"
|
||||
CrashShutdown = "57P02"
|
||||
CannotConnectNow = "57P03"
|
||||
DatabaseDropped = "57P04"
|
||||
IdleSessionTimeout = "57P05"
|
||||
|
||||
// Class 58 — System Error (errors external to PostgreSQL itself)
|
||||
SystemError = "58000"
|
||||
IOError = "58030"
|
||||
UndefinedFile = "58P01"
|
||||
DuplicateFile = "58P02"
|
||||
|
||||
// Class 72 — Snapshot Failure
|
||||
SnapshotTooOld = "72000"
|
||||
|
||||
// Class F0 — Configuration File Error
|
||||
ConfigFileError = "F0000"
|
||||
LockFileExists = "F0001"
|
||||
|
||||
// Class HV — Foreign Data Wrapper Error (SQL/MED)
|
||||
FDWError = "HV000"
|
||||
FDWColumnNameNotFound = "HV005"
|
||||
FDWDynamicParameterValueNeeded = "HV002"
|
||||
FDWFunctionSequenceError = "HV010"
|
||||
FDWInconsistentDescriptorInformation = "HV021"
|
||||
FDWInvalidAttributeValue = "HV024"
|
||||
FDWInvalidColumnName = "HV007"
|
||||
FDWInvalidColumnNumber = "HV008"
|
||||
FDWInvalidDataType = "HV004"
|
||||
FDWInvalidDataTypeDescriptors = "HV006"
|
||||
FDWInvalidDescriptorFieldIdentifier = "HV091"
|
||||
FDWInvalidHandle = "HV00B"
|
||||
FDWInvalidOptionIndex = "HV00C"
|
||||
FDWInvalidOptionName = "HV00D"
|
||||
FDWInvalidStringLengthOrBufferLength = "HV090"
|
||||
FDWInvalidStringFormat = "HV00A"
|
||||
FDWInvalidUseOfNullPointer = "HV009"
|
||||
FDWTooManyHandles = "HV014"
|
||||
FDWOutOfMemory = "HV001"
|
||||
FDWNoSchemas = "HV00P"
|
||||
FDWOptionNameNotFound = "HV00J"
|
||||
FDWReplyHandle = "HV00K"
|
||||
FDWSchemaNotFound = "HV00Q"
|
||||
FDWTableNotFound = "HV00R"
|
||||
FDWUnableToCreateExecution = "HV00L"
|
||||
FDWUnableToCreateReply = "HV00M"
|
||||
FDWUnableToEstablishConnection = "HV00N"
|
||||
|
||||
// Class P0 — PL/pgSQL Error
|
||||
PLpgSQLError = "P0000"
|
||||
RaiseException = "P0001"
|
||||
NoDataFound = "P0002"
|
||||
TooManyRows = "P0003"
|
||||
AssertFailure = "P0004"
|
||||
|
||||
// Class XX — Internal Error
|
||||
InternalError = "XX000"
|
||||
DataCorrupted = "XX001"
|
||||
IndexCorrupted = "XX002"
|
||||
)
|
||||
|
||||
// IsSuccessfulCompletion asserts the error code class is Class 00 — Successful Completion
|
||||
func IsSuccessfulCompletion(code string) bool {
|
||||
switch code {
|
||||
case SuccessfulCompletion:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsWarning asserts the error code class is Class 01 — Warning
|
||||
func IsWarning(code string) bool {
|
||||
switch code {
|
||||
case Warning, DynamicResultSetsReturned, ImplicitZeroBitPadding, NullValueEliminatedInSetFunction, PrivilegeNotGranted, PrivilegeNotRevoked, StringDataRightTruncationWarning, DeprecatedFeature:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsNoData asserts the error code class is Class 02 — No Data (this is also a warning class per the SQL standard)
|
||||
func IsNoData(code string) bool {
|
||||
switch code {
|
||||
case NoData, NoAdditionalDynamicResultSetsReturned:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSQLStatementNotYetComplete asserts the error code class is Class 03 — SQL Statement Not Yet Complete
|
||||
func IsSQLStatementNotYetComplete(code string) bool {
|
||||
switch code {
|
||||
case SQLStatementNotYetComplete:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsConnectionException asserts the error code class is Class 08 — Connection Exception
|
||||
func IsConnectionException(code string) bool {
|
||||
switch code {
|
||||
case ConnectionException, ConnectionDoesNotExist, ConnectionFailure, SQLClientUnableToEstablishSQLConnection, SQLServerRejectedEstablishmentOfSQLConnection, TransactionResolutionUnknown, ProtocolViolation:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsTriggeredActionException asserts the error code class is Class 09 — Triggered Action Exception
|
||||
func IsTriggeredActionException(code string) bool {
|
||||
switch code {
|
||||
case TriggeredActionException:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsFeatureNotSupported asserts the error code class is Class 0A — Feature Not Supported
|
||||
func IsFeatureNotSupported(code string) bool {
|
||||
switch code {
|
||||
case FeatureNotSupported:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInvalidTransactionInitiation asserts the error code class is Class 0B — Invalid Transaction Initiation
|
||||
func IsInvalidTransactionInitiation(code string) bool {
|
||||
switch code {
|
||||
case InvalidTransactionInitiation:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsLocatorException asserts the error code class is Class 0F — Locator Exception
|
||||
func IsLocatorException(code string) bool {
|
||||
switch code {
|
||||
case LocatorException, InvalidLocatorSpecification:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInvalidGrantor asserts the error code class is Class 0L — Invalid Grantor
|
||||
func IsInvalidGrantor(code string) bool {
|
||||
switch code {
|
||||
case InvalidGrantor, InvalidGrantOperation:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInvalidRoleSpecification asserts the error code class is Class 0P — Invalid Role Specification
|
||||
func IsInvalidRoleSpecification(code string) bool {
|
||||
switch code {
|
||||
case InvalidRoleSpecification:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsDiagnosticsException asserts the error code class is Class 0Z — Diagnostics Exception
|
||||
func IsDiagnosticsException(code string) bool {
|
||||
switch code {
|
||||
case DiagnosticsException, StackedDiagnosticsAccessedWithoutActiveHandler:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCaseNotFound asserts the error code class is Class 20 — Case Not Found
|
||||
func IsCaseNotFound(code string) bool {
|
||||
switch code {
|
||||
case CaseNotFound:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCardinalityViolation asserts the error code class is Class 21 — Cardinality Violation
|
||||
func IsCardinalityViolation(code string) bool {
|
||||
switch code {
|
||||
case CardinalityViolation:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsDataException asserts the error code class is Class 22 — Data Exception
|
||||
func IsDataException(code string) bool {
|
||||
switch code {
|
||||
case DataException, ArraySubscriptError, CharacterNotInRepertoire, DatetimeFieldOverflow, DivisionByZero, ErrorInAssignment, EscapeCharacterConflict, IndicatorOverflow, IntervalFieldOverflow, InvalidArgumentForLogarithm, InvalidArgumentForNtileFunction, InvalidArgumentForNthValueFunction, InvalidArgumentForPowerFunction, InvalidArgumentForWidthBucketFunction, InvalidCharacterValueForCast, InvalidDatetimeFormat, InvalidEscapeCharacter, InvalidEscapeOctet, InvalidEscapeSequence, NonstandardUseOfEscapeCharacter, InvalidIndicatorParameterValue, InvalidParameterValue, InvalidPrecedingOrFollowingSize, InvalidRegularExpression, InvalidRowCountInLimitClause, InvalidRowCountInResultOffsetClause, InvalidTablesampleArgument, InvalidTablesampleRepeat, InvalidTimeZoneDisplacementValue, InvalidUseOfEscapeCharacter, MostSpecificTypeMismatch, NullValueNotAllowedDataException, NullValueNoIndicatorParameter, NumericValueOutOfRange, SequenceGeneratorLimitExceeded, StringDataLengthMismatch, StringDataRightTruncationDataException, SubstringError, TrimError, UnterminatedCString, ZeroLengthCharacterString, FloatingPointException, InvalidTextRepresentation, InvalidBinaryRepresentation, BadCopyFileFormat, UntranslatableCharacter, NotAnXMLDocument, InvalidXMLDocument, InvalidXMLContent, InvalidXMLComment, InvalidXMLProcessingInstruction, DuplicateJSONObjectKeyValue, InvalidArgumentForSQLJSONDatetimeFunction, InvalidJSONText, InvalidSQLJSONSubscript, MoreThanOneSQLJSONItem, NoSQLJSONItem, NonNumericSQLJSONItem, NonUniqueKeysInAJSONObject, SingletonSQLJSONItemRequired, SQLJSONArrayNotFound, SQLJSONMemberNotFound, SQLJSONNumberNotFound, SQLJSONObjectNotFound, TooManyJSONArrayElements, TooManyJSONObjectMembers, SQLJSONScalarRequired:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsIntegrityConstraintViolation asserts the error code class is Class 23 — Integrity Constraint Violation
|
||||
func IsIntegrityConstraintViolation(code string) bool {
|
||||
switch code {
|
||||
case IntegrityConstraintViolation, RestrictViolation, NotNullViolation, ForeignKeyViolation, UniqueViolation, CheckViolation, ExclusionViolation:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInvalidCursorState asserts the error code class is Class 24 — Invalid Cursor State
|
||||
func IsInvalidCursorState(code string) bool {
|
||||
switch code {
|
||||
case InvalidCursorState:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInvalidTransactionState asserts the error code class is Class 25 — Invalid Transaction State
|
||||
func IsInvalidTransactionState(code string) bool {
|
||||
switch code {
|
||||
case InvalidTransactionState, ActiveSQLTransaction, BranchTransactionAlreadyActive, HeldCursorRequiresSameIsolationLevel, InappropriateAccessModeForBranchTransaction, InappropriateIsolationLevelForBranchTransaction, NoActiveSQLTransactionForBranchTransaction, ReadOnlySQLTransaction, SchemaAndDataStatementMixingNotSupported, NoActiveSQLTransaction, InFailedSQLTransaction, IdleInTransactionSessionTimeout:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInvalidSQLStatementName asserts the error code class is Class 26 — Invalid SQL Statement Name
|
||||
func IsInvalidSQLStatementName(code string) bool {
|
||||
switch code {
|
||||
case InvalidSQLStatementName:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsTriggeredDataChangeViolation asserts the error code class is Class 27 — Triggered Data Change Violation
|
||||
func IsTriggeredDataChangeViolation(code string) bool {
|
||||
switch code {
|
||||
case TriggeredDataChangeViolation:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInvalidAuthorizationSpecification asserts the error code class is Class 28 — Invalid Authorization Specification
|
||||
func IsInvalidAuthorizationSpecification(code string) bool {
|
||||
switch code {
|
||||
case InvalidAuthorizationSpecification, InvalidPassword:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsDependentPrivilegeDescriptorsStillExist asserts the error code class is Class 2B — Dependent Privilege Descriptors Still Exist
|
||||
func IsDependentPrivilegeDescriptorsStillExist(code string) bool {
|
||||
switch code {
|
||||
case DependentPrivilegeDescriptorsStillExist, DependentObjectsStillExist:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInvalidTransactionTermination asserts the error code class is Class 2D — Invalid Transaction Termination
|
||||
func IsInvalidTransactionTermination(code string) bool {
|
||||
switch code {
|
||||
case InvalidTransactionTermination:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSQLRoutineException asserts the error code class is Class 2F — SQL Routine Exception
|
||||
func IsSQLRoutineException(code string) bool {
|
||||
switch code {
|
||||
case SQLRoutineException, FunctionExecutedNoReturnStatement, ModifyingSQLDataNotPermittedSQLRoutineException, ProhibitedSQLStatementAttemptedSQLRoutineException, ReadingSQLDataNotPermittedSQLRoutineException:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInvalidCursorName asserts the error code class is Class 34 — Invalid Cursor Name
|
||||
func IsInvalidCursorName(code string) bool {
|
||||
switch code {
|
||||
case InvalidCursorName:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsExternalRoutineException asserts the error code class is Class 38 — External Routine Exception
|
||||
func IsExternalRoutineException(code string) bool {
|
||||
switch code {
|
||||
case ExternalRoutineException, ContainingSQLNotPermitted, ModifyingSQLDataNotPermittedExternalRoutineException, ProhibitedSQLStatementAttemptedExternalRoutineException, ReadingSQLDataNotPermittedExternalRoutineException:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsExternalRoutineInvocationException asserts the error code class is Class 39 — External Routine Invocation Exception
|
||||
func IsExternalRoutineInvocationException(code string) bool {
|
||||
switch code {
|
||||
case ExternalRoutineInvocationException, InvalidSQLstateReturned, NullValueNotAllowedExternalRoutineInvocationException, TriggerProtocolViolated, SRFProtocolViolated, EventTriggerProtocolViolated:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSavepointException asserts the error code class is Class 3B — Savepoint Exception
|
||||
func IsSavepointException(code string) bool {
|
||||
switch code {
|
||||
case SavepointException, InvalidSavepointSpecification:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInvalidCatalogName asserts the error code class is Class 3D — Invalid Catalog Name
|
||||
func IsInvalidCatalogName(code string) bool {
|
||||
switch code {
|
||||
case InvalidCatalogName:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInvalidSchemaName asserts the error code class is Class 3F — Invalid Schema Name
|
||||
func IsInvalidSchemaName(code string) bool {
|
||||
switch code {
|
||||
case InvalidSchemaName:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsTransactionRollback asserts the error code class is Class 40 — Transaction Rollback
|
||||
func IsTransactionRollback(code string) bool {
|
||||
switch code {
|
||||
case TransactionRollback, TransactionIntegrityConstraintViolation, SerializationFailure, StatementCompletionUnknown, DeadlockDetected:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSyntaxErrororAccessRuleViolation asserts the error code class is Class 42 — Syntax Error or Access Rule Violation
|
||||
func IsSyntaxErrororAccessRuleViolation(code string) bool {
|
||||
switch code {
|
||||
case SyntaxErrorOrAccessRuleViolation, SyntaxError, InsufficientPrivilege, CannotCoerce, GroupingError, WindowingError, InvalidRecursion, InvalidForeignKey, InvalidName, NameTooLong, ReservedName, DatatypeMismatch, IndeterminateDatatype, CollationMismatch, IndeterminateCollation, WrongObjectType, GeneratedAlways, UndefinedColumn, UndefinedFunction, UndefinedTable, UndefinedParameter, UndefinedObject, DuplicateColumn, DuplicateCursor, DuplicateDatabase, DuplicateFunction, DuplicatePreparedStatement, DuplicateSchema, DuplicateTable, DuplicateAlias, DuplicateObject, AmbiguousColumn, AmbiguousFunction, AmbiguousParameter, AmbiguousAlias, InvalidColumnReference, InvalidColumnDefinition, InvalidCursorDefinition, InvalidDatabaseDefinition, InvalidFunctionDefinition, InvalidPreparedStatementDefinition, InvalidSchemaDefinition, InvalidTableDefinition, InvalidObjectDefinition:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsWithCheckOptionViolation asserts the error code class is Class 44 — WITH CHECK OPTION Violation
|
||||
func IsWithCheckOptionViolation(code string) bool {
|
||||
switch code {
|
||||
case WithCheckOptionViolation:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInsufficientResources asserts the error code class is Class 53 — Insufficient Resources
|
||||
func IsInsufficientResources(code string) bool {
|
||||
switch code {
|
||||
case InsufficientResources, DiskFull, OutOfMemory, TooManyConnections, ConfigurationLimitExceeded:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsProgramLimitExceeded asserts the error code class is Class 54 — Program Limit Exceeded
|
||||
func IsProgramLimitExceeded(code string) bool {
|
||||
switch code {
|
||||
case ProgramLimitExceeded, StatementTooComplex, TooManyColumns, TooManyArguments:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsObjectNotInPrerequisiteState asserts the error code class is Class 55 — Object Not In Prerequisite State
|
||||
func IsObjectNotInPrerequisiteState(code string) bool {
|
||||
switch code {
|
||||
case ObjectNotInPrerequisiteState, ObjectInUse, CantChangeRuntimeParam, LockNotAvailable, UnsafeNewEnumValueUsage:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsOperatorIntervention asserts the error code class is Class 57 — Operator Intervention
|
||||
func IsOperatorIntervention(code string) bool {
|
||||
switch code {
|
||||
case OperatorIntervention, QueryCanceled, AdminShutdown, CrashShutdown, CannotConnectNow, DatabaseDropped, IdleSessionTimeout:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSystemError asserts the error code class is Class 58 — System Error (errors external to PostgreSQL itself)
|
||||
func IsSystemError(code string) bool {
|
||||
switch code {
|
||||
case SystemError, IOError, UndefinedFile, DuplicateFile:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSnapshotFailure asserts the error code class is Class 72 — Snapshot Failure
|
||||
func IsSnapshotFailure(code string) bool {
|
||||
switch code {
|
||||
case SnapshotTooOld:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsConfigurationFileError asserts the error code class is Class F0 — Configuration File Error
|
||||
func IsConfigurationFileError(code string) bool {
|
||||
switch code {
|
||||
case ConfigFileError, LockFileExists:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsForeignDataWrapperError asserts the error code class is Class HV — Foreign Data Wrapper Error (SQL/MED)
|
||||
func IsForeignDataWrapperError(code string) bool {
|
||||
switch code {
|
||||
case FDWError, FDWColumnNameNotFound, FDWDynamicParameterValueNeeded, FDWFunctionSequenceError, FDWInconsistentDescriptorInformation, FDWInvalidAttributeValue, FDWInvalidColumnName, FDWInvalidColumnNumber, FDWInvalidDataType, FDWInvalidDataTypeDescriptors, FDWInvalidDescriptorFieldIdentifier, FDWInvalidHandle, FDWInvalidOptionIndex, FDWInvalidOptionName, FDWInvalidStringLengthOrBufferLength, FDWInvalidStringFormat, FDWInvalidUseOfNullPointer, FDWTooManyHandles, FDWOutOfMemory, FDWNoSchemas, FDWOptionNameNotFound, FDWReplyHandle, FDWSchemaNotFound, FDWTableNotFound, FDWUnableToCreateExecution, FDWUnableToCreateReply, FDWUnableToEstablishConnection:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsPLpgSQLError asserts the error code class is Class P0 — PL/pgSQL Error
|
||||
func IsPLpgSQLError(code string) bool {
|
||||
switch code {
|
||||
case PLpgSQLError, RaiseException, NoDataFound, TooManyRows, AssertFailure:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInternalError asserts the error code class is Class XX — Internal Error
|
||||
func IsInternalError(code string) bool {
|
||||
switch code {
|
||||
case InternalError, DataCorrupted, IndexCorrupted:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
# Run this script against the data in table A.1. on https://www.postgresql.org/docs/14/errcodes-appendix.html.
|
||||
#
|
||||
# Source data should be formatted like the following:
|
||||
#
|
||||
# Class 00 — Successful Completion
|
||||
# 00000 successful_completion
|
||||
# Class 01 — Warning
|
||||
# 01000 warning
|
||||
# 0100C dynamic_result_sets_returned
|
||||
#
|
||||
# for best results pass through gofmt
|
||||
# ruby gen.rb < tablecontents.txt | gofmt > errcode.go
|
||||
|
||||
code_name_overrides = {
|
||||
# Some error code names are repeated. In those cases add the error class as a suffix.
|
||||
"01004" => "StringDataRightTruncationWarning",
|
||||
"22001" => "StringDataRightTruncationDataException",
|
||||
"22004" => "NullValueNotAllowedDataException",
|
||||
"2F002" => "ModifyingSQLDataNotPermittedSQLRoutineException",
|
||||
"2F003" => "ProhibitedSQLStatementAttemptedSQLRoutineException",
|
||||
"2F004" => "ReadingSQLDataNotPermittedSQLRoutineException",
|
||||
"38002" => "ModifyingSQLDataNotPermittedExternalRoutineException",
|
||||
"38003" => "ProhibitedSQLStatementAttemptedExternalRoutineException",
|
||||
"38004" => "ReadingSQLDataNotPermittedExternalRoutineException",
|
||||
"39004" => "NullValueNotAllowedExternalRoutineInvocationException",
|
||||
|
||||
# Go casing corrections
|
||||
"08001" => "SQLClientUnableToEstablishSQLConnection",
|
||||
"08004" => "SQLServerRejectedEstablishmentOfSQLConnection",
|
||||
"P0000" => "PLpgSQLError"
|
||||
}
|
||||
|
||||
class_name_overrides = {
|
||||
# Go casing corrections
|
||||
"WITHCHECKOPTIONViolation" => "WithCheckOptionViolation"
|
||||
}
|
||||
|
||||
cls_errs = Array.new
|
||||
cls_assertions = Array.new
|
||||
last_cls = ""
|
||||
last_cls_full = ""
|
||||
|
||||
def build_assert_func(last_cls, last_cls_full, cls_errs)
|
||||
<<~GO
|
||||
// Is#{last_cls} asserts the error code class is #{last_cls_full}
|
||||
func Is#{last_cls} (code string) bool {
|
||||
switch code{
|
||||
case #{cls_errs.join(", ")}:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
GO
|
||||
end
|
||||
|
||||
puts <<~STR
|
||||
// Package pgerrcode contains constants for PostgreSQL error codes.
|
||||
package pgerrcode
|
||||
|
||||
// Source: https://www.postgresql.org/docs/14/errcodes-appendix.html
|
||||
// See gen.rb for script that can convert the error code table to Go code.
|
||||
|
||||
const (
|
||||
STR
|
||||
|
||||
ARGF.each do |line|
|
||||
case line
|
||||
when /^Class/
|
||||
if cls_errs.length > 0 && last_cls != ""
|
||||
assert_func = build_assert_func(class_name_overrides.fetch(last_cls) { last_cls }, last_cls_full, cls_errs)
|
||||
cls_assertions.push(assert_func)
|
||||
end
|
||||
last_cls = line.split("—")[1]
|
||||
.gsub(" ", "")
|
||||
.gsub("/", "")
|
||||
.gsub("\n", "")
|
||||
.sub(/\(\w*\)/, "")
|
||||
last_cls_full = line.gsub("\n", "")
|
||||
cls_errs.clear
|
||||
puts
|
||||
puts "// #{line}"
|
||||
when /^(\w{5})\s+(\w+)/
|
||||
code = $1
|
||||
name = code_name_overrides.fetch(code) do
|
||||
$2.split("_").map(&:capitalize).join
|
||||
.gsub("Sql", "SQL")
|
||||
.gsub("Xml", "XML")
|
||||
.gsub("Fdw", "FDW")
|
||||
.gsub("Srf", "SRF")
|
||||
.gsub("Io", "IO")
|
||||
.gsub("Json", "JSON")
|
||||
end
|
||||
cls_errs.push(name)
|
||||
puts %Q[#{name} = "#{code}"]
|
||||
else
|
||||
puts line
|
||||
end
|
||||
end
|
||||
puts ")"
|
||||
|
||||
if cls_errs.length > 0
|
||||
assert_func = build_assert_func(class_name_overrides.fetch(last_cls) { last_cls }, last_cls_full, cls_errs)
|
||||
cls_assertions.push(assert_func)
|
||||
end
|
||||
|
||||
cls_assertions.each do |cls_assertion|
|
||||
puts cls_assertion
|
||||
end
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.x
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2019 Jack Christensen
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
[](https://godoc.org/github.com/jackc/pgio)
|
||||
[](https://travis-ci.org/jackc/pgio)
|
||||
|
||||
# pgio
|
||||
|
||||
Package pgio is a low-level toolkit building messages in the PostgreSQL wire protocol.
|
||||
|
||||
pgio provides functions for appending integers to a []byte while doing byte
|
||||
order conversion.
|
||||
|
||||
Extracted from original implementation in https://github.com/jackc/pgx.
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// Package pgio is a low-level toolkit building messages in the PostgreSQL wire protocol.
|
||||
/*
|
||||
pgio provides functions for appending integers to a []byte while doing byte
|
||||
order conversion.
|
||||
*/
|
||||
package pgio
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package pgio
|
||||
|
||||
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))
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.x
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2019 Jack Christensen
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
[](https://godoc.org/github.com/jackc/pgpassfile)
|
||||
[](https://travis-ci.org/jackc/pgpassfile)
|
||||
|
||||
# pgpassfile
|
||||
|
||||
Package pgpassfile is a parser PostgreSQL .pgpass files.
|
||||
|
||||
Extracted and rewritten from original implementation in https://github.com/jackc/pgx.
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
// Package pgpassfile is a parser PostgreSQL .pgpass files.
|
||||
package pgpassfile
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Entry represents a line in a PG passfile.
|
||||
type Entry struct {
|
||||
Hostname string
|
||||
Port string
|
||||
Database string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// Passfile is the in memory data structure representing a PG passfile.
|
||||
type Passfile struct {
|
||||
Entries []*Entry
|
||||
}
|
||||
|
||||
// ReadPassfile reads the file at path and parses it into a Passfile.
|
||||
func ReadPassfile(path string) (*Passfile, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return ParsePassfile(f)
|
||||
}
|
||||
|
||||
// ParsePassfile reads r and parses it into a Passfile.
|
||||
func ParsePassfile(r io.Reader) (*Passfile, error) {
|
||||
passfile := &Passfile{}
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
entry := parseLine(scanner.Text())
|
||||
if entry != nil {
|
||||
passfile.Entries = append(passfile.Entries, entry)
|
||||
}
|
||||
}
|
||||
|
||||
return passfile, scanner.Err()
|
||||
}
|
||||
|
||||
// Match (not colons or escaped colon or escaped backslash)+. Essentially gives a split on unescaped
|
||||
// colon.
|
||||
var colonSplitterRegexp = regexp.MustCompile("(([^:]|(\\:)))+")
|
||||
|
||||
// var colonSplitterRegexp = regexp.MustCompile("((?:[^:]|(?:\\:)|(?:\\\\))+)")
|
||||
|
||||
// parseLine parses a line into an *Entry. It returns nil on comment lines or any other unparsable
|
||||
// line.
|
||||
func parseLine(line string) *Entry {
|
||||
const (
|
||||
tmpBackslash = "\r"
|
||||
tmpColon = "\n"
|
||||
)
|
||||
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
if strings.HasPrefix(line, "#") {
|
||||
return nil
|
||||
}
|
||||
|
||||
line = strings.Replace(line, `\\`, tmpBackslash, -1)
|
||||
line = strings.Replace(line, `\:`, tmpColon, -1)
|
||||
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) != 5 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unescape escaped colons and backslashes
|
||||
for i := range parts {
|
||||
parts[i] = strings.Replace(parts[i], tmpBackslash, `\`, -1)
|
||||
parts[i] = strings.Replace(parts[i], tmpColon, `:`, -1)
|
||||
}
|
||||
|
||||
return &Entry{
|
||||
Hostname: parts[0],
|
||||
Port: parts[1],
|
||||
Database: parts[2],
|
||||
Username: parts[3],
|
||||
Password: parts[4],
|
||||
}
|
||||
}
|
||||
|
||||
// FindPassword finds the password for the provided hostname, port, database, and username. For a
|
||||
// Unix domain socket hostname must be set to "localhost". An empty string will be returned if no
|
||||
// match is found.
|
||||
//
|
||||
// See https://www.postgresql.org/docs/current/libpq-pgpass.html for more password file information.
|
||||
func (pf *Passfile) FindPassword(hostname, port, database, username string) (password string) {
|
||||
for _, e := range pf.Entries {
|
||||
if (e.Hostname == "*" || e.Hostname == hostname) &&
|
||||
(e.Port == "*" || e.Port == port) &&
|
||||
(e.Database == "*" || e.Database == database) &&
|
||||
(e.Username == "*" || e.Username == username) {
|
||||
return e.Password
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.x
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2020 Jack Christensen
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
[](https://godoc.org/github.com/jackc/pgservicefile)
|
||||
[](https://travis-ci.org/jackc/pgservicefile)
|
||||
|
||||
# pgservicefile
|
||||
|
||||
Package pgservicefile is a parser for PostgreSQL service files (e.g. `.pg_service.conf`).
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
// Package pgservicefile is a parser for PostgreSQL service files (e.g. .pg_service.conf).
|
||||
package pgservicefile
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
Name string
|
||||
Settings map[string]string
|
||||
}
|
||||
|
||||
type Servicefile struct {
|
||||
Services []*Service
|
||||
servicesByName map[string]*Service
|
||||
}
|
||||
|
||||
// GetService returns the named service.
|
||||
func (sf *Servicefile) GetService(name string) (*Service, error) {
|
||||
service, present := sf.servicesByName[name]
|
||||
if !present {
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
return service, nil
|
||||
}
|
||||
|
||||
// ReadServicefile reads the file at path and parses it into a Servicefile.
|
||||
func ReadServicefile(path string) (*Servicefile, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return ParseServicefile(f)
|
||||
}
|
||||
|
||||
// ParseServicefile reads r and parses it into a Servicefile.
|
||||
func ParseServicefile(r io.Reader) (*Servicefile, error) {
|
||||
servicefile := &Servicefile{}
|
||||
|
||||
var service *Service
|
||||
scanner := bufio.NewScanner(r)
|
||||
lineNum := 0
|
||||
for scanner.Scan() {
|
||||
lineNum += 1
|
||||
line := scanner.Text()
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
// ignore comments and empty lines
|
||||
} else if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
|
||||
service = &Service{Name: line[1 : len(line)-1], Settings: make(map[string]string)}
|
||||
servicefile.Services = append(servicefile.Services, service)
|
||||
} else {
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("unable to parse line %d", lineNum)
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
|
||||
service.Settings[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
servicefile.servicesByName = make(map[string]*Service, len(servicefile.Services))
|
||||
for _, service := range servicefile.Services {
|
||||
servicefile.servicesByName[service.Name] = service
|
||||
}
|
||||
|
||||
return servicefile, scanner.Err()
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
# 1.13.0 (December 1, 2022)
|
||||
|
||||
* Fix: Reset jsonb before unmarshal (Tomas Odinas)
|
||||
* Fix: return correct zero value when UUID conversion fails (ndrpnt)
|
||||
* Fix: EncodeText for Lseg includes [ and ]
|
||||
* Support sql Value and Scan for custom date type (Hubert Krauze)
|
||||
* Support Ltree binary encoding (AmineChikhaoui)
|
||||
* Fix: dates with "BC" (jozeflami)
|
||||
|
||||
# 1.12.0 (August 6, 2022)
|
||||
|
||||
* Add JSONArray (Jakob Ackermann)
|
||||
* Support Inet from fmt.Stringer and encoding.TextMarshaler (Ville Skyttä)
|
||||
* Support UUID from fmt.Stringer interface (Lasse Hyldahl Jensen)
|
||||
* Fix: shopspring-numeric extension does not panic on NaN
|
||||
* Numeric can be assigned to string
|
||||
* Fix: Do not send IPv4 networks as IPv4-mapped IPv6 (William Storey)
|
||||
* Fix: PlanScan for interface{}(nil) (James Hartig)
|
||||
* Fix: *sql.Scanner for NULL handling (James Hartig)
|
||||
* Timestamp[tz].Set() supports string (Harmen)
|
||||
* Fix: Hstore AssignTo with map of *string (Diego Becciolini)
|
||||
|
||||
# 1.11.0 (April 21, 2022)
|
||||
|
||||
* Add multirange for numeric, int4, and int8 (Vu)
|
||||
* JSONBArray now supports json.RawMessage (Jens Emil Schulz Østergaard)
|
||||
* Add RecordArray (WGH)
|
||||
* Add UnmarshalJSON to pgtype.Int2
|
||||
* Hstore.Set accepts map[string]Text
|
||||
|
||||
# 1.10.0 (February 7, 2022)
|
||||
|
||||
* Normalize UTC timestamps to comply with stdlib (Torkel Rogstad)
|
||||
* Assign Numeric to *big.Rat (Oleg Lomaka)
|
||||
* Fix typo in float8 error message (Pinank Solanki)
|
||||
* Scan type aliases for floating point types (Collin Forsyth)
|
||||
|
||||
# 1.9.1 (November 28, 2021)
|
||||
|
||||
* Fix: binary timestamp is assumed to be in UTC (restored behavior changed in v1.9.0)
|
||||
|
||||
# 1.9.0 (November 20, 2021)
|
||||
|
||||
* Fix binary hstore null decoding
|
||||
* Add shopspring/decimal.NullDecimal support to integration (Eli Treuherz)
|
||||
* Inet.Set supports bare IP address (Carl Dunham)
|
||||
* Add zeronull.Float8
|
||||
* Fix NULL being lost when scanning unknown OID into sql.Scanner
|
||||
* Fix BPChar.AssignTo **rune
|
||||
* Add support for fmt.Stringer and driver.Valuer in String fields encoding (Jan Dubsky)
|
||||
* Fix really big timestamp(tz)s binary format parsing (e.g. year 294276) (Jim Tsao)
|
||||
* Support `map[string]*string` as hstore (Adrian Sieger)
|
||||
* Fix parsing text array with negative bounds
|
||||
* Add infinity support for numeric (Jim Tsao)
|
||||
|
||||
# 1.8.1 (July 24, 2021)
|
||||
|
||||
* Cleaned up Go module dependency chain
|
||||
|
||||
# 1.8.0 (July 10, 2021)
|
||||
|
||||
* Maintain host bits for inet types (Cameron Daniel)
|
||||
* Support pointers of wrapping structs (Ivan Daunis)
|
||||
* Register JSONBArray at NewConnInfo() (Rueian)
|
||||
* CompositeTextScanner handles backslash escapes
|
||||
|
||||
# 1.7.0 (March 25, 2021)
|
||||
|
||||
* Fix scanning int into **sql.Scanner implementor
|
||||
* Add tsrange array type (Vasilii Novikov)
|
||||
* Fix: escaped strings when they start or end with a newline char (Stephane Martin)
|
||||
* Accept nil *time.Time in Time.Set
|
||||
* Fix numeric NaN support
|
||||
* Use Go 1.13 errors instead of xerrors
|
||||
|
||||
# 1.6.2 (December 3, 2020)
|
||||
|
||||
* Fix panic on assigning empty array to non-slice or array
|
||||
* Fix text array parsing disambiguates NULL and "NULL"
|
||||
* Fix Timestamptz.DecodeText with too short text
|
||||
|
||||
# 1.6.1 (October 31, 2020)
|
||||
|
||||
* Fix simple protocol empty array support
|
||||
|
||||
# 1.6.0 (October 24, 2020)
|
||||
|
||||
* Fix AssignTo pointer to pointer to slice and named types.
|
||||
* Fix zero length array assignment (Simo Haasanen)
|
||||
* Add float64, float32 convert to int2, int4, int8 (lqu3j)
|
||||
* Support setting infinite timestamps (Erik Agsjö)
|
||||
* Polygon improvements (duohedron)
|
||||
* Fix Inet.Set with nil (Tomas Volf)
|
||||
|
||||
# 1.5.0 (September 26, 2020)
|
||||
|
||||
* Add slice of slice mapping to multi-dimensional arrays (Simo Haasanen)
|
||||
* Fix JSONBArray
|
||||
* Fix selecting empty array
|
||||
* Text formatted values except bytea can be directly scanned to []byte
|
||||
* Add JSON marshalling for UUID (bakmataliev)
|
||||
* Improve point type conversions (bakmataliev)
|
||||
|
||||
# 1.4.2 (July 22, 2020)
|
||||
|
||||
* Fix encoding of a large composite data type (Yaz Saito)
|
||||
|
||||
# 1.4.1 (July 14, 2020)
|
||||
|
||||
* Fix ArrayType DecodeBinary empty array breaks future reads
|
||||
|
||||
# 1.4.0 (June 27, 2020)
|
||||
|
||||
* Add JSON support to ext/gofrs-uuid
|
||||
* Performance improvements in Scan path
|
||||
* Improved ext/shopspring-numeric binary decoding performance
|
||||
* Add composite type support (Maxim Ivanov and Jack Christensen)
|
||||
* Add better generic enum type support
|
||||
* Add generic array type support
|
||||
* Clarify and normalize Value semantics
|
||||
* Fix hstore with empty string values
|
||||
* Numeric supports NaN values (leighhopcroft)
|
||||
* Add slice of pointer support to array types (megaturbo)
|
||||
* Add jsonb array type (tserakhau)
|
||||
* Allow converting intervals with months and days to duration
|
||||
|
||||
# 1.3.0 (March 30, 2020)
|
||||
|
||||
* Get implemented on T instead of *T
|
||||
* Set will call Get on src if possible
|
||||
* Range types Set method supports its own type, string, and nil
|
||||
* Date.Set parses string
|
||||
* Fix correct format verb for unknown type error (Robert Welin)
|
||||
* Truncate nanoseconds in EncodeText for Timestamptz and Timestamp
|
||||
|
||||
# 1.2.0 (February 5, 2020)
|
||||
|
||||
* Add zeronull package for easier NULL <-> zero conversion
|
||||
* Add JSON marshalling for shopspring-numeric extension
|
||||
* Add JSON marshalling for Bool, Date, JSON/B, Timestamptz (Jeffrey Stiles)
|
||||
* Fix null status in UnmarshalJSON for some types (Jeffrey Stiles)
|
||||
|
||||
# 1.1.0 (January 11, 2020)
|
||||
|
||||
* Add PostgreSQL time type support
|
||||
* Add more automatic conversions of integer arrays of different types (Jean-Philippe Quéméner)
|
||||
|
||||
# 1.0.3 (November 16, 2019)
|
||||
|
||||
* Support initializing Array types from a slice of the value (Alex Gaynor)
|
||||
|
||||
# 1.0.2 (October 22, 2019)
|
||||
|
||||
* Fix scan into null into pointer to pointer implementing Decode* interface. (Jeremy Altavilla)
|
||||
|
||||
# 1.0.1 (September 19, 2019)
|
||||
|
||||
* Fix daterange OID
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2013-2021 Jack Christensen
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
[](https://godoc.org/github.com/jackc/pgtype)
|
||||

|
||||
|
||||
---
|
||||
|
||||
This version is used with pgx `v4`. In pgx `v5` it is part of the https://github.com/jackc/pgx repository.
|
||||
|
||||
---
|
||||
|
||||
# pgtype
|
||||
|
||||
pgtype implements Go types for over 70 PostgreSQL types. pgtype is the type system underlying the
|
||||
https://github.com/jackc/pgx PostgreSQL driver. These types support the binary format for enhanced performance with pgx.
|
||||
They also support the database/sql `Scan` and `Value` interfaces and can be used with https://github.com/lib/pq.
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
if src == nil {
|
||||
*dst = ACLItem{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
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 fmt.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 fmt.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.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 fmt.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
|
||||
}
|
||||
}
|
||||
+428
@@ -0,0 +1,428 @@
|
||||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
case []ACLItem:
|
||||
if value == nil {
|
||||
*dst = ACLItemArray{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = ACLItemArray{Status: Present}
|
||||
} else {
|
||||
*dst = ACLItemArray{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = ACLItemArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for ACLItemArray", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = ACLItemArray{Status: Present}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to ACLItemArray", src)
|
||||
}
|
||||
|
||||
*dst = ACLItemArray{
|
||||
Elements: make([]ACLItem, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Status: Present,
|
||||
}
|
||||
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
// Maybe the target was one dimension too far, try again:
|
||||
if len(dst.Dimensions) > 1 {
|
||||
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
|
||||
elementsLength = 0
|
||||
for _, dim := range dst.Dimensions {
|
||||
if elementsLength == 0 {
|
||||
elementsLength = int(dim.Length)
|
||||
} else {
|
||||
elementsLength *= int(dim.Length)
|
||||
}
|
||||
}
|
||||
dst.Elements = make([]ACLItem, elementsLength)
|
||||
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to ACLItemArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *ACLItemArray) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to ACLItemArray")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in ACLItemArray", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, 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:
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (src *ACLItemArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from ACLItemArray")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from ACLItemArray")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
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" || uta.Quoted[i] {
|
||||
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 fmt.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
|
||||
}
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
// 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, fmt.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, fmt.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
|
||||
Quoted []bool
|
||||
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, fmt.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, fmt.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
if r == '=' {
|
||||
break
|
||||
} else if r != '[' {
|
||||
return nil, fmt.Errorf("invalid array, expected '[' or '=' got %v", r)
|
||||
}
|
||||
|
||||
lower, err := arrayParseInteger(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
if r != ':' {
|
||||
return nil, fmt.Errorf("invalid array, expected ':' got %v", r)
|
||||
}
|
||||
|
||||
upper, err := arrayParseInteger(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
if r != ']' {
|
||||
return nil, fmt.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, fmt.Errorf("invalid array: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if r != '{' {
|
||||
return nil, fmt.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, fmt.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, fmt.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, quoted, err := arrayParseValue(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array value: %v", err)
|
||||
}
|
||||
if currentDim == counterDim {
|
||||
implicitDimensions[currentDim].Length++
|
||||
}
|
||||
dst.Quoted = append(dst.Quoted, quoted)
|
||||
dst.Elements = append(dst.Elements, value)
|
||||
}
|
||||
|
||||
if currentDim < 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
skipWhitespace(buf)
|
||||
|
||||
if buf.Len() > 0 {
|
||||
return nil, fmt.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, bool, error) {
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if r == '"' {
|
||||
return arrayParseQuotedValue(buf)
|
||||
}
|
||||
buf.UnreadRune()
|
||||
|
||||
s := &bytes.Buffer{}
|
||||
|
||||
for {
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
switch r {
|
||||
case ',', '}':
|
||||
buf.UnreadRune()
|
||||
return s.String(), false, nil
|
||||
}
|
||||
|
||||
s.WriteRune(r)
|
||||
}
|
||||
}
|
||||
|
||||
func arrayParseQuotedValue(buf *bytes.Buffer) (string, bool, error) {
|
||||
s := &bytes.Buffer{}
|
||||
|
||||
for {
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
switch r {
|
||||
case '\\':
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
case '"':
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
buf.UnreadRune()
|
||||
return s.String(), true, 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') || r == '-' {
|
||||
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 isSpace(ch byte) bool {
|
||||
// see https://github.com/postgres/postgres/blob/REL_12_STABLE/src/backend/parser/scansup.c#L224
|
||||
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\f'
|
||||
}
|
||||
|
||||
func QuoteArrayElementIfNeeded(src string) string {
|
||||
if src == "" || (len(src) == 4 && strings.ToLower(src) == "null") || isSpace(src[0]) || isSpace(src[len(src)-1]) || strings.ContainsAny(src, `{},"\`) {
|
||||
return quoteArrayElement(src)
|
||||
}
|
||||
return src
|
||||
}
|
||||
|
||||
func findDimensionsFromValue(value reflect.Value, dimensions []ArrayDimension, elementsLength int) ([]ArrayDimension, int, bool) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
length := value.Len()
|
||||
if 0 == elementsLength {
|
||||
elementsLength = length
|
||||
} else {
|
||||
elementsLength *= length
|
||||
}
|
||||
dimensions = append(dimensions, ArrayDimension{Length: int32(length), LowerBound: 1})
|
||||
for i := 0; i < length; i++ {
|
||||
if d, l, ok := findDimensionsFromValue(value.Index(i), dimensions, elementsLength); ok {
|
||||
return d, l, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return dimensions, elementsLength, true
|
||||
}
|
||||
+353
@@ -0,0 +1,353 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
// ArrayType represents an array type. While it implements Value, this is only in service of its type conversion duties
|
||||
// when registered as a data type in a ConnType. It should not be used directly as a Value. ArrayType is a convenience
|
||||
// type for types that do not have a concrete array type.
|
||||
type ArrayType struct {
|
||||
elements []ValueTranscoder
|
||||
dimensions []ArrayDimension
|
||||
|
||||
typeName string
|
||||
newElement func() ValueTranscoder
|
||||
|
||||
elementOID uint32
|
||||
status Status
|
||||
}
|
||||
|
||||
func NewArrayType(typeName string, elementOID uint32, newElement func() ValueTranscoder) *ArrayType {
|
||||
return &ArrayType{typeName: typeName, elementOID: elementOID, newElement: newElement}
|
||||
}
|
||||
|
||||
func (at *ArrayType) NewTypeValue() Value {
|
||||
return &ArrayType{
|
||||
elements: at.elements,
|
||||
dimensions: at.dimensions,
|
||||
status: at.status,
|
||||
|
||||
typeName: at.typeName,
|
||||
elementOID: at.elementOID,
|
||||
newElement: at.newElement,
|
||||
}
|
||||
}
|
||||
|
||||
func (at *ArrayType) TypeName() string {
|
||||
return at.typeName
|
||||
}
|
||||
|
||||
func (dst *ArrayType) setNil() {
|
||||
dst.elements = nil
|
||||
dst.dimensions = nil
|
||||
dst.status = Null
|
||||
}
|
||||
|
||||
func (dst *ArrayType) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
dst.setNil()
|
||||
return nil
|
||||
}
|
||||
|
||||
sliceVal := reflect.ValueOf(src)
|
||||
if sliceVal.Kind() != reflect.Slice {
|
||||
return fmt.Errorf("cannot set non-slice")
|
||||
}
|
||||
|
||||
if sliceVal.IsNil() {
|
||||
dst.setNil()
|
||||
return nil
|
||||
}
|
||||
|
||||
dst.elements = make([]ValueTranscoder, sliceVal.Len())
|
||||
for i := range dst.elements {
|
||||
v := dst.newElement()
|
||||
err := v.Set(sliceVal.Index(i).Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dst.elements[i] = v
|
||||
}
|
||||
dst.dimensions = []ArrayDimension{{Length: int32(len(dst.elements)), LowerBound: 1}}
|
||||
dst.status = Present
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst ArrayType) Get() interface{} {
|
||||
switch dst.status {
|
||||
case Present:
|
||||
elementValues := make([]interface{}, len(dst.elements))
|
||||
for i := range dst.elements {
|
||||
elementValues[i] = dst.elements[i].Get()
|
||||
}
|
||||
return elementValues
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *ArrayType) AssignTo(dst interface{}) error {
|
||||
ptrSlice := reflect.ValueOf(dst)
|
||||
if ptrSlice.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("cannot assign to non-pointer")
|
||||
}
|
||||
|
||||
sliceVal := ptrSlice.Elem()
|
||||
sliceType := sliceVal.Type()
|
||||
|
||||
if sliceType.Kind() != reflect.Slice {
|
||||
return fmt.Errorf("cannot assign to pointer to non-slice")
|
||||
}
|
||||
|
||||
switch src.status {
|
||||
case Present:
|
||||
slice := reflect.MakeSlice(sliceType, len(src.elements), len(src.elements))
|
||||
elemType := sliceType.Elem()
|
||||
|
||||
for i := range src.elements {
|
||||
ptrElem := reflect.New(elemType)
|
||||
err := src.elements[i].AssignTo(ptrElem.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
slice.Index(i).Set(ptrElem.Elem())
|
||||
}
|
||||
|
||||
sliceVal.Set(slice)
|
||||
return nil
|
||||
case Null:
|
||||
sliceVal.Set(reflect.Zero(sliceType))
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *ArrayType) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
dst.setNil()
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []ValueTranscoder
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]ValueTranscoder, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
elem := dst.newElement()
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
dst.elements = elements
|
||||
dst.dimensions = uta.Dimensions
|
||||
dst.status = Present
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *ArrayType) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
dst.setNil()
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []ValueTranscoder
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
dst.elements = elements
|
||||
dst.dimensions = arrayHeader.Dimensions
|
||||
dst.status = Present
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements = make([]ValueTranscoder, elementCount)
|
||||
|
||||
for i := range elements {
|
||||
elem := dst.newElement()
|
||||
elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
|
||||
rp += 4
|
||||
var elemSrc []byte
|
||||
if elemLen >= 0 {
|
||||
elemSrc = src[rp : rp+elemLen]
|
||||
rp += elemLen
|
||||
}
|
||||
err = elem.DecodeBinary(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
|
||||
dst.elements = elements
|
||||
dst.dimensions = arrayHeader.Dimensions
|
||||
dst.status = Present
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src ArrayType) 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 ArrayType) 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,
|
||||
ElementOID: int32(src.elementOID),
|
||||
}
|
||||
|
||||
for i := range src.elements {
|
||||
if src.elements[i].Get() == nil {
|
||||
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 *ArrayType) 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 fmt.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src ArrayType) 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
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
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)
|
||||
}
|
||||
|
||||
func (dst *Bit) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
return (*Varbit)(dst).DecodeText(ci, src)
|
||||
}
|
||||
|
||||
func (src Bit) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
return (Varbit)(src).EncodeText(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()
|
||||
}
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Bool struct {
|
||||
Bool bool
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Bool) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Bool{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
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}
|
||||
case *bool:
|
||||
if value == nil {
|
||||
*dst = Bool{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *string:
|
||||
if value == nil {
|
||||
*dst = Bool{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
default:
|
||||
if originalSrc, ok := underlyingBoolType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.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 fmt.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.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 fmt.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 fmt.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 fmt.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
|
||||
}
|
||||
}
|
||||
|
||||
func (src Bool) MarshalJSON() ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
if src.Bool {
|
||||
return []byte("true"), nil
|
||||
} else {
|
||||
return []byte("false"), nil
|
||||
}
|
||||
case Null:
|
||||
return []byte("null"), nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return nil, errBadStatus
|
||||
}
|
||||
|
||||
func (dst *Bool) UnmarshalJSON(b []byte) error {
|
||||
var v *bool
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v == nil {
|
||||
*dst = Bool{Status: Null}
|
||||
} else {
|
||||
*dst = Bool{Bool: *v, Status: Present}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+517
@@ -0,0 +1,517 @@
|
||||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
case []Bool:
|
||||
if value == nil {
|
||||
*dst = BoolArray{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = BoolArray{Status: Present}
|
||||
} else {
|
||||
*dst = BoolArray{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = BoolArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for BoolArray", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = BoolArray{Status: Present}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to BoolArray", src)
|
||||
}
|
||||
|
||||
*dst = BoolArray{
|
||||
Elements: make([]Bool, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Status: Present,
|
||||
}
|
||||
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
// Maybe the target was one dimension too far, try again:
|
||||
if len(dst.Dimensions) > 1 {
|
||||
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
|
||||
elementsLength = 0
|
||||
for _, dim := range dst.Dimensions {
|
||||
if elementsLength == 0 {
|
||||
elementsLength = int(dim.Length)
|
||||
} else {
|
||||
elementsLength *= int(dim.Length)
|
||||
}
|
||||
}
|
||||
dst.Elements = make([]Bool, elementsLength)
|
||||
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to BoolArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *BoolArray) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to BoolArray")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in BoolArray", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, 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:
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (src *BoolArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from BoolArray")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from BoolArray")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
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" || uta.Quoted[i] {
|
||||
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, fmt.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 fmt.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
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Box struct {
|
||||
P [2]Vec2
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Box) Set(src interface{}) error {
|
||||
return fmt.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 fmt.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 fmt.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 fmt.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 fmt.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src Box) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
case *rune:
|
||||
runes := []rune(src.String)
|
||||
if len(runes) == 1 {
|
||||
*v = runes[0]
|
||||
return nil
|
||||
}
|
||||
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 fmt.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (BPChar) PreferredResultFormat() int16 {
|
||||
return TextFormatCode
|
||||
}
|
||||
|
||||
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 (BPChar) PreferredParamFormat() int16 {
|
||||
return TextFormatCode
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
+517
@@ -0,0 +1,517 @@
|
||||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
case []BPChar:
|
||||
if value == nil {
|
||||
*dst = BPCharArray{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = BPCharArray{Status: Present}
|
||||
} else {
|
||||
*dst = BPCharArray{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = BPCharArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for BPCharArray", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = BPCharArray{Status: Present}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to BPCharArray", src)
|
||||
}
|
||||
|
||||
*dst = BPCharArray{
|
||||
Elements: make([]BPChar, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Status: Present,
|
||||
}
|
||||
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
// Maybe the target was one dimension too far, try again:
|
||||
if len(dst.Dimensions) > 1 {
|
||||
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
|
||||
elementsLength = 0
|
||||
for _, dim := range dst.Dimensions {
|
||||
if elementsLength == 0 {
|
||||
elementsLength = int(dim.Length)
|
||||
} else {
|
||||
elementsLength *= int(dim.Length)
|
||||
}
|
||||
}
|
||||
dst.Elements = make([]BPChar, elementsLength)
|
||||
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to BPCharArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *BPCharArray) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to BPCharArray")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in BPCharArray", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, 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:
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (src *BPCharArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from BPCharArray")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from BPCharArray")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
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" || uta.Quoted[i] {
|
||||
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, fmt.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 fmt.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
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Bytea struct {
|
||||
Bytes []byte
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Bytea) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Bytea{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
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 fmt.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 fmt.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.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 fmt.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 fmt.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
|
||||
}
|
||||
}
|
||||
+489
@@ -0,0 +1,489 @@
|
||||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
case []Bytea:
|
||||
if value == nil {
|
||||
*dst = ByteaArray{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = ByteaArray{Status: Present}
|
||||
} else {
|
||||
*dst = ByteaArray{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = ByteaArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for ByteaArray", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = ByteaArray{Status: Present}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to ByteaArray", src)
|
||||
}
|
||||
|
||||
*dst = ByteaArray{
|
||||
Elements: make([]Bytea, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Status: Present,
|
||||
}
|
||||
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
// Maybe the target was one dimension too far, try again:
|
||||
if len(dst.Dimensions) > 1 {
|
||||
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
|
||||
elementsLength = 0
|
||||
for _, dim := range dst.Dimensions {
|
||||
if elementsLength == 0 {
|
||||
elementsLength = int(dim.Length)
|
||||
} else {
|
||||
elementsLength *= int(dim.Length)
|
||||
}
|
||||
}
|
||||
dst.Elements = make([]Bytea, elementsLength)
|
||||
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to ByteaArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *ByteaArray) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to ByteaArray")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in ByteaArray", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, 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:
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (src *ByteaArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from ByteaArray")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from ByteaArray")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
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" || uta.Quoted[i] {
|
||||
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, fmt.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 fmt.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
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
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()
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
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)
|
||||
}
|
||||
+546
@@ -0,0 +1,546 @@
|
||||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
case []CIDR:
|
||||
if value == nil {
|
||||
*dst = CIDRArray{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = CIDRArray{Status: Present}
|
||||
} else {
|
||||
*dst = CIDRArray{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = CIDRArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for CIDRArray", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = CIDRArray{Status: Present}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to CIDRArray", src)
|
||||
}
|
||||
|
||||
*dst = CIDRArray{
|
||||
Elements: make([]CIDR, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Status: Present,
|
||||
}
|
||||
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
// Maybe the target was one dimension too far, try again:
|
||||
if len(dst.Dimensions) > 1 {
|
||||
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
|
||||
elementsLength = 0
|
||||
for _, dim := range dst.Dimensions {
|
||||
if elementsLength == 0 {
|
||||
elementsLength = int(dim.Length)
|
||||
} else {
|
||||
elementsLength *= int(dim.Length)
|
||||
}
|
||||
}
|
||||
dst.Elements = make([]CIDR, elementsLength)
|
||||
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to CIDRArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *CIDRArray) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to CIDRArray")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in CIDRArray", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, 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:
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (src *CIDRArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from CIDRArray")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from CIDRArray")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
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" || uta.Quoted[i] {
|
||||
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, fmt.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 fmt.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
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Circle struct {
|
||||
P Vec2
|
||||
R float64
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Circle) Set(src interface{}) error {
|
||||
return fmt.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 fmt.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 fmt.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 fmt.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 fmt.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src Circle) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
package pgtype
|
||||
|
||||
import "fmt"
|
||||
|
||||
// CompositeFields scans the fields of a composite type into the elements of the CompositeFields value. To scan a
|
||||
// nullable value use a *CompositeFields. It will be set to nil in case of null.
|
||||
//
|
||||
// CompositeFields implements EncodeBinary and EncodeText. However, functionality is limited due to CompositeFields not
|
||||
// knowing the PostgreSQL schema of the composite type. Prefer using a registered CompositeType.
|
||||
type CompositeFields []interface{}
|
||||
|
||||
func (cf CompositeFields) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if len(cf) == 0 {
|
||||
return fmt.Errorf("cannot decode into empty CompositeFields")
|
||||
}
|
||||
|
||||
if src == nil {
|
||||
return fmt.Errorf("cannot decode unexpected null into CompositeFields")
|
||||
}
|
||||
|
||||
scanner := NewCompositeBinaryScanner(ci, src)
|
||||
|
||||
for _, f := range cf {
|
||||
scanner.ScanValue(f)
|
||||
}
|
||||
|
||||
if scanner.Err() != nil {
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cf CompositeFields) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if len(cf) == 0 {
|
||||
return fmt.Errorf("cannot decode into empty CompositeFields")
|
||||
}
|
||||
|
||||
if src == nil {
|
||||
return fmt.Errorf("cannot decode unexpected null into CompositeFields")
|
||||
}
|
||||
|
||||
scanner := NewCompositeTextScanner(ci, src)
|
||||
|
||||
for _, f := range cf {
|
||||
scanner.ScanValue(f)
|
||||
}
|
||||
|
||||
if scanner.Err() != nil {
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeText encodes composite fields into the text format. Prefer registering a CompositeType to using
|
||||
// CompositeFields to encode directly.
|
||||
func (cf CompositeFields) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
b := NewCompositeTextBuilder(ci, buf)
|
||||
|
||||
for _, f := range cf {
|
||||
if textEncoder, ok := f.(TextEncoder); ok {
|
||||
b.AppendEncoder(textEncoder)
|
||||
} else {
|
||||
b.AppendValue(f)
|
||||
}
|
||||
}
|
||||
|
||||
return b.Finish()
|
||||
}
|
||||
|
||||
// EncodeBinary encodes composite fields into the binary format. Unlike CompositeType the schema of the destination is
|
||||
// unknown. Prefer registering a CompositeType to using CompositeFields to encode directly. Because the binary
|
||||
// composite format requires the OID of each field to be specified the only types that will work are those known to
|
||||
// ConnInfo.
|
||||
//
|
||||
// In particular:
|
||||
//
|
||||
// * Nil cannot be used because there is no way to determine what type it.
|
||||
// * Integer types must be exact matches. e.g. A Go int32 into a PostgreSQL bigint will fail.
|
||||
// * No dereferencing will be done. e.g. *Text must be used instead of Text.
|
||||
func (cf CompositeFields) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
b := NewCompositeBinaryBuilder(ci, buf)
|
||||
|
||||
for _, f := range cf {
|
||||
dt, ok := ci.DataTypeForValue(f)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Unknown OID for %#v", f)
|
||||
}
|
||||
|
||||
if binaryEncoder, ok := f.(BinaryEncoder); ok {
|
||||
b.AppendEncoder(dt.OID, binaryEncoder)
|
||||
} else {
|
||||
err := dt.Value.Set(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if binaryEncoder, ok := dt.Value.(BinaryEncoder); ok {
|
||||
b.AppendEncoder(dt.OID, binaryEncoder)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Cannot encode binary format for %v", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.Finish()
|
||||
}
|
||||
+682
@@ -0,0 +1,682 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type CompositeTypeField struct {
|
||||
Name string
|
||||
OID uint32
|
||||
}
|
||||
|
||||
type CompositeType struct {
|
||||
status Status
|
||||
|
||||
typeName string
|
||||
|
||||
fields []CompositeTypeField
|
||||
valueTranscoders []ValueTranscoder
|
||||
}
|
||||
|
||||
// NewCompositeType creates a CompositeType from fields and ci. ci is used to find the ValueTranscoders used
|
||||
// for fields. All field OIDs must be previously registered in ci.
|
||||
func NewCompositeType(typeName string, fields []CompositeTypeField, ci *ConnInfo) (*CompositeType, error) {
|
||||
valueTranscoders := make([]ValueTranscoder, len(fields))
|
||||
|
||||
for i := range fields {
|
||||
dt, ok := ci.DataTypeForOID(fields[i].OID)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no data type registered for oid: %d", fields[i].OID)
|
||||
}
|
||||
|
||||
value := NewValue(dt.Value)
|
||||
valueTranscoder, ok := value.(ValueTranscoder)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("data type for oid does not implement ValueTranscoder: %d", fields[i].OID)
|
||||
}
|
||||
|
||||
valueTranscoders[i] = valueTranscoder
|
||||
}
|
||||
|
||||
return &CompositeType{typeName: typeName, fields: fields, valueTranscoders: valueTranscoders}, nil
|
||||
}
|
||||
|
||||
// NewCompositeTypeValues creates a CompositeType from fields and values. fields and values must have the same length.
|
||||
// Prefer NewCompositeType unless overriding the transcoding of fields is required.
|
||||
func NewCompositeTypeValues(typeName string, fields []CompositeTypeField, values []ValueTranscoder) (*CompositeType, error) {
|
||||
if len(fields) != len(values) {
|
||||
return nil, errors.New("fields and valueTranscoders must have same length")
|
||||
}
|
||||
|
||||
return &CompositeType{typeName: typeName, fields: fields, valueTranscoders: values}, nil
|
||||
}
|
||||
|
||||
func (src CompositeType) Get() interface{} {
|
||||
switch src.status {
|
||||
case Present:
|
||||
results := make(map[string]interface{}, len(src.valueTranscoders))
|
||||
for i := range src.valueTranscoders {
|
||||
results[src.fields[i].Name] = src.valueTranscoders[i].Get()
|
||||
}
|
||||
return results
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return src.status
|
||||
}
|
||||
}
|
||||
|
||||
func (ct *CompositeType) NewTypeValue() Value {
|
||||
a := &CompositeType{
|
||||
typeName: ct.typeName,
|
||||
fields: ct.fields,
|
||||
valueTranscoders: make([]ValueTranscoder, len(ct.valueTranscoders)),
|
||||
}
|
||||
|
||||
for i := range ct.valueTranscoders {
|
||||
a.valueTranscoders[i] = NewValue(ct.valueTranscoders[i]).(ValueTranscoder)
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func (ct *CompositeType) TypeName() string {
|
||||
return ct.typeName
|
||||
}
|
||||
|
||||
func (ct *CompositeType) Fields() []CompositeTypeField {
|
||||
return ct.fields
|
||||
}
|
||||
|
||||
func (dst *CompositeType) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
dst.status = Null
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case []interface{}:
|
||||
if len(value) != len(dst.valueTranscoders) {
|
||||
return fmt.Errorf("Number of fields don't match. CompositeType has %d fields", len(dst.valueTranscoders))
|
||||
}
|
||||
for i, v := range value {
|
||||
if err := dst.valueTranscoders[i].Set(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
dst.status = Present
|
||||
case *[]interface{}:
|
||||
if value == nil {
|
||||
dst.status = Null
|
||||
return nil
|
||||
}
|
||||
return dst.Set(*value)
|
||||
default:
|
||||
return fmt.Errorf("Can not convert %v to Composite", src)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssignTo should never be called on composite value directly
|
||||
func (src CompositeType) AssignTo(dst interface{}) error {
|
||||
switch src.status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
case []interface{}:
|
||||
if len(v) != len(src.valueTranscoders) {
|
||||
return fmt.Errorf("Number of fields don't match. CompositeType has %d fields", len(src.valueTranscoders))
|
||||
}
|
||||
for i := range src.valueTranscoders {
|
||||
if v[i] == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
err := assignToOrSet(src.valueTranscoders[i], v[i])
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to assign to dst[%d]: %v", i, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case *[]interface{}:
|
||||
return src.AssignTo(*v)
|
||||
default:
|
||||
if isPtrStruct, err := src.assignToPtrStruct(dst); isPtrStruct {
|
||||
return err
|
||||
}
|
||||
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return fmt.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func assignToOrSet(src Value, dst interface{}) error {
|
||||
assignToErr := src.AssignTo(dst)
|
||||
if assignToErr != nil {
|
||||
// Try to use get / set instead -- this avoids every type having to be able to AssignTo type of self.
|
||||
setSucceeded := false
|
||||
if setter, ok := dst.(Value); ok {
|
||||
err := setter.Set(src.Get())
|
||||
setSucceeded = err == nil
|
||||
}
|
||||
if !setSucceeded {
|
||||
return assignToErr
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src CompositeType) assignToPtrStruct(dst interface{}) (bool, error) {
|
||||
dstValue := reflect.ValueOf(dst)
|
||||
if dstValue.Kind() != reflect.Ptr {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if dstValue.IsNil() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
dstElemValue := dstValue.Elem()
|
||||
dstElemType := dstElemValue.Type()
|
||||
|
||||
if dstElemType.Kind() != reflect.Struct {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
exportedFields := make([]int, 0, dstElemType.NumField())
|
||||
for i := 0; i < dstElemType.NumField(); i++ {
|
||||
sf := dstElemType.Field(i)
|
||||
if sf.PkgPath == "" {
|
||||
exportedFields = append(exportedFields, i)
|
||||
}
|
||||
}
|
||||
|
||||
if len(exportedFields) != len(src.valueTranscoders) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
for i := range exportedFields {
|
||||
err := assignToOrSet(src.valueTranscoders[i], dstElemValue.Field(exportedFields[i]).Addr().Interface())
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("unable to assign to field %s: %v", dstElemType.Field(exportedFields[i]).Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (src CompositeType) EncodeBinary(ci *ConnInfo, buf []byte) (newBuf []byte, err error) {
|
||||
switch src.status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
b := NewCompositeBinaryBuilder(ci, buf)
|
||||
for i := range src.valueTranscoders {
|
||||
b.AppendEncoder(src.fields[i].OID, src.valueTranscoders[i])
|
||||
}
|
||||
|
||||
return b.Finish()
|
||||
}
|
||||
|
||||
// DecodeBinary implements BinaryDecoder interface.
|
||||
// Opposite to Record, fields in a composite act as a "schema"
|
||||
// and decoding fails if SQL value can't be assigned due to
|
||||
// type mismatch
|
||||
func (dst *CompositeType) DecodeBinary(ci *ConnInfo, buf []byte) error {
|
||||
if buf == nil {
|
||||
dst.status = Null
|
||||
return nil
|
||||
}
|
||||
|
||||
scanner := NewCompositeBinaryScanner(ci, buf)
|
||||
|
||||
for _, f := range dst.valueTranscoders {
|
||||
scanner.ScanDecoder(f)
|
||||
}
|
||||
|
||||
if scanner.Err() != nil {
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
dst.status = Present
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *CompositeType) DecodeText(ci *ConnInfo, buf []byte) error {
|
||||
if buf == nil {
|
||||
dst.status = Null
|
||||
return nil
|
||||
}
|
||||
|
||||
scanner := NewCompositeTextScanner(ci, buf)
|
||||
|
||||
for _, f := range dst.valueTranscoders {
|
||||
scanner.ScanDecoder(f)
|
||||
}
|
||||
|
||||
if scanner.Err() != nil {
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
dst.status = Present
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src CompositeType) EncodeText(ci *ConnInfo, buf []byte) (newBuf []byte, err error) {
|
||||
switch src.status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
b := NewCompositeTextBuilder(ci, buf)
|
||||
for _, f := range src.valueTranscoders {
|
||||
b.AppendEncoder(f)
|
||||
}
|
||||
|
||||
return b.Finish()
|
||||
}
|
||||
|
||||
type CompositeBinaryScanner struct {
|
||||
ci *ConnInfo
|
||||
rp int
|
||||
src []byte
|
||||
|
||||
fieldCount int32
|
||||
fieldBytes []byte
|
||||
fieldOID uint32
|
||||
err error
|
||||
}
|
||||
|
||||
// NewCompositeBinaryScanner a scanner over a binary encoded composite balue.
|
||||
func NewCompositeBinaryScanner(ci *ConnInfo, src []byte) *CompositeBinaryScanner {
|
||||
rp := 0
|
||||
if len(src[rp:]) < 4 {
|
||||
return &CompositeBinaryScanner{err: fmt.Errorf("Record incomplete %v", src)}
|
||||
}
|
||||
|
||||
fieldCount := int32(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
|
||||
return &CompositeBinaryScanner{
|
||||
ci: ci,
|
||||
rp: rp,
|
||||
src: src,
|
||||
fieldCount: fieldCount,
|
||||
}
|
||||
}
|
||||
|
||||
// ScanDecoder calls Next and decodes the result with d.
|
||||
func (cfs *CompositeBinaryScanner) ScanDecoder(d BinaryDecoder) {
|
||||
if cfs.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if cfs.Next() {
|
||||
cfs.err = d.DecodeBinary(cfs.ci, cfs.fieldBytes)
|
||||
} else {
|
||||
cfs.err = errors.New("read past end of composite")
|
||||
}
|
||||
}
|
||||
|
||||
// ScanDecoder calls Next and scans the result into d.
|
||||
func (cfs *CompositeBinaryScanner) ScanValue(d interface{}) {
|
||||
if cfs.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if cfs.Next() {
|
||||
cfs.err = cfs.ci.Scan(cfs.OID(), BinaryFormatCode, cfs.Bytes(), d)
|
||||
} else {
|
||||
cfs.err = errors.New("read past end of composite")
|
||||
}
|
||||
}
|
||||
|
||||
// Next advances the scanner to the next field. It returns false after the last field is read or an error occurs. After
|
||||
// Next returns false, the Err method can be called to check if any errors occurred.
|
||||
func (cfs *CompositeBinaryScanner) Next() bool {
|
||||
if cfs.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if cfs.rp == len(cfs.src) {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(cfs.src[cfs.rp:]) < 8 {
|
||||
cfs.err = fmt.Errorf("Record incomplete %v", cfs.src)
|
||||
return false
|
||||
}
|
||||
cfs.fieldOID = binary.BigEndian.Uint32(cfs.src[cfs.rp:])
|
||||
cfs.rp += 4
|
||||
|
||||
fieldLen := int(int32(binary.BigEndian.Uint32(cfs.src[cfs.rp:])))
|
||||
cfs.rp += 4
|
||||
|
||||
if fieldLen >= 0 {
|
||||
if len(cfs.src[cfs.rp:]) < fieldLen {
|
||||
cfs.err = fmt.Errorf("Record incomplete rp=%d src=%v", cfs.rp, cfs.src)
|
||||
return false
|
||||
}
|
||||
cfs.fieldBytes = cfs.src[cfs.rp : cfs.rp+fieldLen]
|
||||
cfs.rp += fieldLen
|
||||
} else {
|
||||
cfs.fieldBytes = nil
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfs *CompositeBinaryScanner) FieldCount() int {
|
||||
return int(cfs.fieldCount)
|
||||
}
|
||||
|
||||
// Bytes returns the bytes of the field most recently read by Scan().
|
||||
func (cfs *CompositeBinaryScanner) Bytes() []byte {
|
||||
return cfs.fieldBytes
|
||||
}
|
||||
|
||||
// OID returns the OID of the field most recently read by Scan().
|
||||
func (cfs *CompositeBinaryScanner) OID() uint32 {
|
||||
return cfs.fieldOID
|
||||
}
|
||||
|
||||
// Err returns any error encountered by the scanner.
|
||||
func (cfs *CompositeBinaryScanner) Err() error {
|
||||
return cfs.err
|
||||
}
|
||||
|
||||
type CompositeTextScanner struct {
|
||||
ci *ConnInfo
|
||||
rp int
|
||||
src []byte
|
||||
|
||||
fieldBytes []byte
|
||||
err error
|
||||
}
|
||||
|
||||
// NewCompositeTextScanner a scanner over a text encoded composite value.
|
||||
func NewCompositeTextScanner(ci *ConnInfo, src []byte) *CompositeTextScanner {
|
||||
if len(src) < 2 {
|
||||
return &CompositeTextScanner{err: fmt.Errorf("Record incomplete %v", src)}
|
||||
}
|
||||
|
||||
if src[0] != '(' {
|
||||
return &CompositeTextScanner{err: fmt.Errorf("composite text format must start with '('")}
|
||||
}
|
||||
|
||||
if src[len(src)-1] != ')' {
|
||||
return &CompositeTextScanner{err: fmt.Errorf("composite text format must end with ')'")}
|
||||
}
|
||||
|
||||
return &CompositeTextScanner{
|
||||
ci: ci,
|
||||
rp: 1,
|
||||
src: src,
|
||||
}
|
||||
}
|
||||
|
||||
// ScanDecoder calls Next and decodes the result with d.
|
||||
func (cfs *CompositeTextScanner) ScanDecoder(d TextDecoder) {
|
||||
if cfs.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if cfs.Next() {
|
||||
cfs.err = d.DecodeText(cfs.ci, cfs.fieldBytes)
|
||||
} else {
|
||||
cfs.err = errors.New("read past end of composite")
|
||||
}
|
||||
}
|
||||
|
||||
// ScanDecoder calls Next and scans the result into d.
|
||||
func (cfs *CompositeTextScanner) ScanValue(d interface{}) {
|
||||
if cfs.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if cfs.Next() {
|
||||
cfs.err = cfs.ci.Scan(0, TextFormatCode, cfs.Bytes(), d)
|
||||
} else {
|
||||
cfs.err = errors.New("read past end of composite")
|
||||
}
|
||||
}
|
||||
|
||||
// Next advances the scanner to the next field. It returns false after the last field is read or an error occurs. After
|
||||
// Next returns false, the Err method can be called to check if any errors occurred.
|
||||
func (cfs *CompositeTextScanner) Next() bool {
|
||||
if cfs.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if cfs.rp == len(cfs.src) {
|
||||
return false
|
||||
}
|
||||
|
||||
switch cfs.src[cfs.rp] {
|
||||
case ',', ')': // null
|
||||
cfs.rp++
|
||||
cfs.fieldBytes = nil
|
||||
return true
|
||||
case '"': // quoted value
|
||||
cfs.rp++
|
||||
cfs.fieldBytes = make([]byte, 0, 16)
|
||||
for {
|
||||
ch := cfs.src[cfs.rp]
|
||||
|
||||
if ch == '"' {
|
||||
cfs.rp++
|
||||
if cfs.src[cfs.rp] == '"' {
|
||||
cfs.fieldBytes = append(cfs.fieldBytes, '"')
|
||||
cfs.rp++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
} else if ch == '\\' {
|
||||
cfs.rp++
|
||||
cfs.fieldBytes = append(cfs.fieldBytes, cfs.src[cfs.rp])
|
||||
cfs.rp++
|
||||
} else {
|
||||
cfs.fieldBytes = append(cfs.fieldBytes, ch)
|
||||
cfs.rp++
|
||||
}
|
||||
}
|
||||
cfs.rp++
|
||||
return true
|
||||
default: // unquoted value
|
||||
start := cfs.rp
|
||||
for {
|
||||
ch := cfs.src[cfs.rp]
|
||||
if ch == ',' || ch == ')' {
|
||||
break
|
||||
}
|
||||
cfs.rp++
|
||||
}
|
||||
cfs.fieldBytes = cfs.src[start:cfs.rp]
|
||||
cfs.rp++
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes returns the bytes of the field most recently read by Scan().
|
||||
func (cfs *CompositeTextScanner) Bytes() []byte {
|
||||
return cfs.fieldBytes
|
||||
}
|
||||
|
||||
// Err returns any error encountered by the scanner.
|
||||
func (cfs *CompositeTextScanner) Err() error {
|
||||
return cfs.err
|
||||
}
|
||||
|
||||
type CompositeBinaryBuilder struct {
|
||||
ci *ConnInfo
|
||||
buf []byte
|
||||
startIdx int
|
||||
fieldCount uint32
|
||||
err error
|
||||
}
|
||||
|
||||
func NewCompositeBinaryBuilder(ci *ConnInfo, buf []byte) *CompositeBinaryBuilder {
|
||||
startIdx := len(buf)
|
||||
buf = append(buf, 0, 0, 0, 0) // allocate room for number of fields
|
||||
return &CompositeBinaryBuilder{ci: ci, buf: buf, startIdx: startIdx}
|
||||
}
|
||||
|
||||
func (b *CompositeBinaryBuilder) AppendValue(oid uint32, field interface{}) {
|
||||
if b.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dt, ok := b.ci.DataTypeForOID(oid)
|
||||
if !ok {
|
||||
b.err = fmt.Errorf("unknown data type for OID: %d", oid)
|
||||
return
|
||||
}
|
||||
|
||||
err := dt.Value.Set(field)
|
||||
if err != nil {
|
||||
b.err = err
|
||||
return
|
||||
}
|
||||
|
||||
binaryEncoder, ok := dt.Value.(BinaryEncoder)
|
||||
if !ok {
|
||||
b.err = fmt.Errorf("unable to encode binary for OID: %d", oid)
|
||||
return
|
||||
}
|
||||
|
||||
b.AppendEncoder(oid, binaryEncoder)
|
||||
}
|
||||
|
||||
func (b *CompositeBinaryBuilder) AppendEncoder(oid uint32, field BinaryEncoder) {
|
||||
if b.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b.buf = pgio.AppendUint32(b.buf, oid)
|
||||
lengthPos := len(b.buf)
|
||||
b.buf = pgio.AppendInt32(b.buf, -1)
|
||||
fieldBuf, err := field.EncodeBinary(b.ci, b.buf)
|
||||
if err != nil {
|
||||
b.err = err
|
||||
return
|
||||
}
|
||||
if fieldBuf != nil {
|
||||
binary.BigEndian.PutUint32(fieldBuf[lengthPos:], uint32(len(fieldBuf)-len(b.buf)))
|
||||
b.buf = fieldBuf
|
||||
}
|
||||
|
||||
b.fieldCount++
|
||||
}
|
||||
|
||||
func (b *CompositeBinaryBuilder) Finish() ([]byte, error) {
|
||||
if b.err != nil {
|
||||
return nil, b.err
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint32(b.buf[b.startIdx:], b.fieldCount)
|
||||
return b.buf, nil
|
||||
}
|
||||
|
||||
type CompositeTextBuilder struct {
|
||||
ci *ConnInfo
|
||||
buf []byte
|
||||
startIdx int
|
||||
fieldCount uint32
|
||||
err error
|
||||
fieldBuf [32]byte
|
||||
}
|
||||
|
||||
func NewCompositeTextBuilder(ci *ConnInfo, buf []byte) *CompositeTextBuilder {
|
||||
buf = append(buf, '(') // allocate room for number of fields
|
||||
return &CompositeTextBuilder{ci: ci, buf: buf}
|
||||
}
|
||||
|
||||
func (b *CompositeTextBuilder) AppendValue(field interface{}) {
|
||||
if b.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if field == nil {
|
||||
b.buf = append(b.buf, ',')
|
||||
return
|
||||
}
|
||||
|
||||
dt, ok := b.ci.DataTypeForValue(field)
|
||||
if !ok {
|
||||
b.err = fmt.Errorf("unknown data type for field: %v", field)
|
||||
return
|
||||
}
|
||||
|
||||
err := dt.Value.Set(field)
|
||||
if err != nil {
|
||||
b.err = err
|
||||
return
|
||||
}
|
||||
|
||||
textEncoder, ok := dt.Value.(TextEncoder)
|
||||
if !ok {
|
||||
b.err = fmt.Errorf("unable to encode text for value: %v", field)
|
||||
return
|
||||
}
|
||||
|
||||
b.AppendEncoder(textEncoder)
|
||||
}
|
||||
|
||||
func (b *CompositeTextBuilder) AppendEncoder(field TextEncoder) {
|
||||
if b.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fieldBuf, err := field.EncodeText(b.ci, b.fieldBuf[0:0])
|
||||
if err != nil {
|
||||
b.err = err
|
||||
return
|
||||
}
|
||||
if fieldBuf != nil {
|
||||
b.buf = append(b.buf, quoteCompositeFieldIfNeeded(string(fieldBuf))...)
|
||||
}
|
||||
|
||||
b.buf = append(b.buf, ',')
|
||||
}
|
||||
|
||||
func (b *CompositeTextBuilder) Finish() ([]byte, error) {
|
||||
if b.err != nil {
|
||||
return nil, b.err
|
||||
}
|
||||
|
||||
b.buf[len(b.buf)-1] = ')'
|
||||
return b.buf, nil
|
||||
}
|
||||
|
||||
var quoteCompositeReplacer = strings.NewReplacer(`\`, `\\`, `"`, `\"`)
|
||||
|
||||
func quoteCompositeField(src string) string {
|
||||
return `"` + quoteCompositeReplacer.Replace(src) + `"`
|
||||
}
|
||||
|
||||
func quoteCompositeFieldIfNeeded(src string) string {
|
||||
if src == "" || src[0] == ' ' || src[len(src)-1] == ' ' || strings.ContainsAny(src, `(),"\`) {
|
||||
return quoteCompositeField(src)
|
||||
}
|
||||
return src
|
||||
}
|
||||
+476
@@ -0,0 +1,476 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
maxUint = ^uint(0)
|
||||
maxInt = int(maxUint >> 1)
|
||||
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 nil, 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 nil, false
|
||||
}
|
||||
|
||||
// underlyingUUIDType gets the underlying type that can be converted to [16]byte
|
||||
func underlyingUUIDType(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
|
||||
}
|
||||
|
||||
uuidType := reflect.TypeOf([16]byte{})
|
||||
if refVal.Type().ConvertibleTo(uuidType) {
|
||||
return refVal.Convert(uuidType).Interface(), true
|
||||
}
|
||||
|
||||
return nil, 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 fmt.Errorf("%d is less than minimum value for int", srcVal)
|
||||
} else if srcVal > int64(maxInt) {
|
||||
return fmt.Errorf("%d is greater than maximum value for int", srcVal)
|
||||
}
|
||||
*v = int(srcVal)
|
||||
case *int8:
|
||||
if srcVal < math.MinInt8 {
|
||||
return fmt.Errorf("%d is less than minimum value for int8", srcVal)
|
||||
} else if srcVal > math.MaxInt8 {
|
||||
return fmt.Errorf("%d is greater than maximum value for int8", srcVal)
|
||||
}
|
||||
*v = int8(srcVal)
|
||||
case *int16:
|
||||
if srcVal < math.MinInt16 {
|
||||
return fmt.Errorf("%d is less than minimum value for int16", srcVal)
|
||||
} else if srcVal > math.MaxInt16 {
|
||||
return fmt.Errorf("%d is greater than maximum value for int16", srcVal)
|
||||
}
|
||||
*v = int16(srcVal)
|
||||
case *int32:
|
||||
if srcVal < math.MinInt32 {
|
||||
return fmt.Errorf("%d is less than minimum value for int32", srcVal)
|
||||
} else if srcVal > math.MaxInt32 {
|
||||
return fmt.Errorf("%d is greater than maximum value for int32", srcVal)
|
||||
}
|
||||
*v = int32(srcVal)
|
||||
case *int64:
|
||||
if srcVal < math.MinInt64 {
|
||||
return fmt.Errorf("%d is less than minimum value for int64", srcVal)
|
||||
} else if srcVal > math.MaxInt64 {
|
||||
return fmt.Errorf("%d is greater than maximum value for int64", srcVal)
|
||||
}
|
||||
*v = int64(srcVal)
|
||||
case *uint:
|
||||
if srcVal < 0 {
|
||||
return fmt.Errorf("%d is less than zero for uint", srcVal)
|
||||
} else if uint64(srcVal) > uint64(maxUint) {
|
||||
return fmt.Errorf("%d is greater than maximum value for uint", srcVal)
|
||||
}
|
||||
*v = uint(srcVal)
|
||||
case *uint8:
|
||||
if srcVal < 0 {
|
||||
return fmt.Errorf("%d is less than zero for uint8", srcVal)
|
||||
} else if srcVal > math.MaxUint8 {
|
||||
return fmt.Errorf("%d is greater than maximum value for uint8", srcVal)
|
||||
}
|
||||
*v = uint8(srcVal)
|
||||
case *uint16:
|
||||
if srcVal < 0 {
|
||||
return fmt.Errorf("%d is less than zero for uint32", srcVal)
|
||||
} else if srcVal > math.MaxUint16 {
|
||||
return fmt.Errorf("%d is greater than maximum value for uint16", srcVal)
|
||||
}
|
||||
*v = uint16(srcVal)
|
||||
case *uint32:
|
||||
if srcVal < 0 {
|
||||
return fmt.Errorf("%d is less than zero for uint32", srcVal)
|
||||
} else if srcVal > math.MaxUint32 {
|
||||
return fmt.Errorf("%d is greater than maximum value for uint32", srcVal)
|
||||
}
|
||||
*v = uint32(srcVal)
|
||||
case *uint64:
|
||||
if srcVal < 0 {
|
||||
return fmt.Errorf("%d is less than zero for uint64", srcVal)
|
||||
}
|
||||
*v = uint64(srcVal)
|
||||
case sql.Scanner:
|
||||
return v.Scan(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 fmt.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 fmt.Errorf("%d is less than zero for %T", srcVal, dst)
|
||||
}
|
||||
if el.OverflowUint(uint64(srcVal)) {
|
||||
return fmt.Errorf("cannot put %d into %T", srcVal, dst)
|
||||
}
|
||||
el.SetUint(uint64(srcVal))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.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 fmt.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 type alias of a float32 or 64, set dst val
|
||||
case reflect.Float32, reflect.Float64:
|
||||
el.SetFloat(srcVal)
|
||||
return nil
|
||||
// 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 fmt.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 fmt.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 &nullAssignmentError{dst: dst}
|
||||
}
|
||||
|
||||
dstVal := dstPtr.Elem()
|
||||
|
||||
switch dstVal.Kind() {
|
||||
case reflect.Ptr, reflect.Slice, reflect.Map:
|
||||
dstVal.Set(reflect.Zero(dstVal.Type()))
|
||||
return nil
|
||||
}
|
||||
|
||||
return &nullAssignmentError{dst: dst}
|
||||
}
|
||||
|
||||
var kindTypes map[reflect.Kind]reflect.Type
|
||||
|
||||
func toInterface(dst reflect.Value, t reflect.Type) (interface{}, bool) {
|
||||
nextDst := dst.Convert(t)
|
||||
return nextDst.Interface(), dst.Type() != nextDst.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 {
|
||||
return toInterface(dstPtr, reflect.PtrTo(baseValType))
|
||||
}
|
||||
|
||||
if dstVal.Kind() == reflect.Slice {
|
||||
if baseElemType, ok := kindTypes[dstVal.Type().Elem().Kind()]; ok {
|
||||
return toInterface(dstPtr, reflect.PtrTo(reflect.SliceOf(baseElemType)))
|
||||
}
|
||||
}
|
||||
|
||||
if dstVal.Kind() == reflect.Array {
|
||||
if baseElemType, ok := kindTypes[dstVal.Type().Elem().Kind()]; ok {
|
||||
return toInterface(dstPtr, reflect.PtrTo(reflect.ArrayOf(dstVal.Len(), baseElemType)))
|
||||
}
|
||||
}
|
||||
|
||||
if dstVal.Kind() == reflect.Struct {
|
||||
if dstVal.Type().NumField() == 1 && dstVal.Type().Field(0).Anonymous {
|
||||
dstPtr = dstVal.Field(0).Addr()
|
||||
nested := dstVal.Type().Field(0).Type
|
||||
if nested.Kind() == reflect.Array {
|
||||
if baseElemType, ok := kindTypes[nested.Elem().Kind()]; ok {
|
||||
return toInterface(dstPtr, reflect.PtrTo(reflect.ArrayOf(nested.Len(), baseElemType)))
|
||||
}
|
||||
}
|
||||
if _, ok := kindTypes[nested.Kind()]; ok && dstPtr.CanInterface() {
|
||||
return dstPtr.Interface(), true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(""),
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Value() (driver.Value, error) }); ok {
|
||||
v, err := value.Value()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get value %v for Date: %v", value, err)
|
||||
}
|
||||
return dst.Set(v)
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case time.Time:
|
||||
*dst = Date{Time: value, Status: Present}
|
||||
case *time.Time:
|
||||
if value == nil {
|
||||
*dst = Date{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(value))
|
||||
case *string:
|
||||
if value == nil {
|
||||
*dst = Date{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
default:
|
||||
if originalSrc, ok := underlyingTimeType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.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 {
|
||||
if scanner, ok := dst.(sql.Scanner); ok {
|
||||
var err error
|
||||
switch src.Status {
|
||||
case Present:
|
||||
if src.InfinityModifier != None {
|
||||
err = scanner.Scan(src.InfinityModifier.String())
|
||||
} else {
|
||||
err = scanner.Scan(src.Time)
|
||||
}
|
||||
case Null:
|
||||
err = scanner.Scan(nil)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable assign %v to %T: %s", src, dst, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch src.Status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
case *time.Time:
|
||||
if src.InfinityModifier != None {
|
||||
return fmt.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 fmt.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.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:
|
||||
if strings.HasSuffix(sbuf, " BC") {
|
||||
t, err := time.ParseInLocation("2006-01-02", strings.TrimRight(sbuf, " BC"), time.UTC)
|
||||
t2 := time.Date(1-t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = Date{Time: t2, Status: Present}
|
||||
return nil
|
||||
}
|
||||
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 fmt.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 fmt.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
|
||||
}
|
||||
}
|
||||
|
||||
func (src Date) MarshalJSON() ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return []byte("null"), nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
if src.Status != Present {
|
||||
return nil, errBadStatus
|
||||
}
|
||||
|
||||
var s string
|
||||
|
||||
switch src.InfinityModifier {
|
||||
case None:
|
||||
s = src.Time.Format("2006-01-02")
|
||||
case Infinity:
|
||||
s = "infinity"
|
||||
case NegativeInfinity:
|
||||
s = "-infinity"
|
||||
}
|
||||
|
||||
return json.Marshal(s)
|
||||
}
|
||||
|
||||
func (dst *Date) UnmarshalJSON(b []byte) error {
|
||||
var s *string
|
||||
err := json.Unmarshal(b, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s == nil {
|
||||
*dst = Date{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch *s {
|
||||
case "infinity":
|
||||
*dst = Date{Status: Present, InfinityModifier: Infinity}
|
||||
case "-infinity":
|
||||
*dst = Date{Status: Present, InfinityModifier: -Infinity}
|
||||
default:
|
||||
t, err := time.ParseInLocation("2006-01-02", *s, time.UTC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Date{Time: t, Status: Present}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+518
@@ -0,0 +1,518 @@
|
||||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
case []Date:
|
||||
if value == nil {
|
||||
*dst = DateArray{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = DateArray{Status: Present}
|
||||
} else {
|
||||
*dst = DateArray{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = DateArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for DateArray", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = DateArray{Status: Present}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to DateArray", src)
|
||||
}
|
||||
|
||||
*dst = DateArray{
|
||||
Elements: make([]Date, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Status: Present,
|
||||
}
|
||||
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
// Maybe the target was one dimension too far, try again:
|
||||
if len(dst.Dimensions) > 1 {
|
||||
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
|
||||
elementsLength = 0
|
||||
for _, dim := range dst.Dimensions {
|
||||
if elementsLength == 0 {
|
||||
elementsLength = int(dim.Length)
|
||||
} else {
|
||||
elementsLength *= int(dim.Length)
|
||||
}
|
||||
}
|
||||
dst.Elements = make([]Date, elementsLength)
|
||||
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to DateArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *DateArray) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to DateArray")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in DateArray", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, 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:
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (src *DateArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from DateArray")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from DateArray")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
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" || uta.Quoted[i] {
|
||||
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, fmt.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 fmt.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
|
||||
}
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Daterange struct {
|
||||
Lower Date
|
||||
Upper Date
|
||||
LowerType BoundType
|
||||
UpperType BoundType
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Daterange) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = Daterange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case Daterange:
|
||||
*dst = value
|
||||
case *Daterange:
|
||||
*dst = *value
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(value))
|
||||
default:
|
||||
return fmt.Errorf("cannot convert %v to Daterange", src)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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 fmt.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, fmt.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, fmt.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, fmt.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, fmt.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, fmt.Errorf("unknown LowerType: %v", src.LowerType)
|
||||
}
|
||||
|
||||
switch src.UpperType {
|
||||
case Inclusive:
|
||||
rangeType |= upperInclusiveMask
|
||||
case Unbounded:
|
||||
rangeType |= upperUnboundedMask
|
||||
case Exclusive:
|
||||
default:
|
||||
return nil, fmt.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, fmt.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, fmt.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 fmt.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src Daterange) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
+428
@@ -0,0 +1,428 @@
|
||||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
case []GenericText:
|
||||
if value == nil {
|
||||
*dst = EnumArray{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = EnumArray{Status: Present}
|
||||
} else {
|
||||
*dst = EnumArray{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = EnumArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for EnumArray", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = EnumArray{Status: Present}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to EnumArray", src)
|
||||
}
|
||||
|
||||
*dst = EnumArray{
|
||||
Elements: make([]GenericText, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Status: Present,
|
||||
}
|
||||
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
// Maybe the target was one dimension too far, try again:
|
||||
if len(dst.Dimensions) > 1 {
|
||||
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
|
||||
elementsLength = 0
|
||||
for _, dim := range dst.Dimensions {
|
||||
if elementsLength == 0 {
|
||||
elementsLength = int(dim.Length)
|
||||
} else {
|
||||
elementsLength *= int(dim.Length)
|
||||
}
|
||||
}
|
||||
dst.Elements = make([]GenericText, elementsLength)
|
||||
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to EnumArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *EnumArray) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to EnumArray")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in EnumArray", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, 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:
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (src *EnumArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from EnumArray")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from EnumArray")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
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" || uta.Quoted[i] {
|
||||
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 fmt.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
|
||||
}
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
package pgtype
|
||||
|
||||
import "fmt"
|
||||
|
||||
// EnumType represents an enum type. While it implements Value, this is only in service of its type conversion duties
|
||||
// when registered as a data type in a ConnType. It should not be used directly as a Value.
|
||||
type EnumType struct {
|
||||
value string
|
||||
status Status
|
||||
|
||||
typeName string // PostgreSQL type name
|
||||
members []string // enum members
|
||||
membersMap map[string]string // map to quickly lookup member and reuse string instead of allocating
|
||||
}
|
||||
|
||||
// NewEnumType initializes a new EnumType. It retains a read-only reference to members. members must not be changed.
|
||||
func NewEnumType(typeName string, members []string) *EnumType {
|
||||
et := &EnumType{typeName: typeName, members: members}
|
||||
et.membersMap = make(map[string]string, len(members))
|
||||
for _, m := range members {
|
||||
et.membersMap[m] = m
|
||||
}
|
||||
return et
|
||||
}
|
||||
|
||||
func (et *EnumType) NewTypeValue() Value {
|
||||
return &EnumType{
|
||||
value: et.value,
|
||||
status: et.status,
|
||||
|
||||
typeName: et.typeName,
|
||||
members: et.members,
|
||||
membersMap: et.membersMap,
|
||||
}
|
||||
}
|
||||
|
||||
func (et *EnumType) TypeName() string {
|
||||
return et.typeName
|
||||
}
|
||||
|
||||
func (et *EnumType) Members() []string {
|
||||
return et.members
|
||||
}
|
||||
|
||||
// Set assigns src to dst. Set purposely does not check that src is a member. This allows continued error free
|
||||
// operation in the event the PostgreSQL enum type is modified during a connection.
|
||||
func (dst *EnumType) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
dst.status = Null
|
||||
return nil
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case string:
|
||||
dst.value = value
|
||||
dst.status = Present
|
||||
case *string:
|
||||
if value == nil {
|
||||
dst.status = Null
|
||||
} else {
|
||||
dst.value = *value
|
||||
dst.status = Present
|
||||
}
|
||||
case []byte:
|
||||
if value == nil {
|
||||
dst.status = Null
|
||||
} else {
|
||||
dst.value = string(value)
|
||||
dst.status = Present
|
||||
}
|
||||
default:
|
||||
if originalSrc, ok := underlyingStringType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to enum %s", value, dst.typeName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst EnumType) Get() interface{} {
|
||||
switch dst.status {
|
||||
case Present:
|
||||
return dst.value
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *EnumType) AssignTo(dst interface{}) error {
|
||||
switch src.status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
case *string:
|
||||
*v = src.value
|
||||
return nil
|
||||
case *[]byte:
|
||||
*v = make([]byte, len(src.value))
|
||||
copy(*v, src.value)
|
||||
return nil
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return fmt.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (EnumType) PreferredResultFormat() int16 {
|
||||
return TextFormatCode
|
||||
}
|
||||
|
||||
func (dst *EnumType) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
dst.status = Null
|
||||
return nil
|
||||
}
|
||||
|
||||
// Lookup the string in membersMap to avoid an allocation.
|
||||
if s, found := dst.membersMap[string(src)]; found {
|
||||
dst.value = s
|
||||
} else {
|
||||
// If an enum type is modified after the initial connection it is possible to receive an unexpected value.
|
||||
// Gracefully handle this situation. Purposely NOT modifying members and membersMap to allow for sharing members
|
||||
// and membersMap between connections.
|
||||
dst.value = string(src)
|
||||
}
|
||||
dst.status = Present
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *EnumType) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
return dst.DecodeText(ci, src)
|
||||
}
|
||||
|
||||
func (EnumType) PreferredParamFormat() int16 {
|
||||
return TextFormatCode
|
||||
}
|
||||
|
||||
func (src EnumType) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return append(buf, src.value...), nil
|
||||
}
|
||||
|
||||
func (src EnumType) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
return src.EncodeText(ci, buf)
|
||||
}
|
||||
+282
@@ -0,0 +1,282 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Float4 struct {
|
||||
Float float32
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Float4) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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 fmt.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}
|
||||
case *float64:
|
||||
if value == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *float32:
|
||||
if value == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int8:
|
||||
if value == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint8:
|
||||
if value == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int16:
|
||||
if value == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint16:
|
||||
if value == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int32:
|
||||
if value == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint32:
|
||||
if value == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int64:
|
||||
if value == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint64:
|
||||
if value == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int:
|
||||
if value == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint:
|
||||
if value == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *string:
|
||||
if value == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
default:
|
||||
if originalSrc, ok := underlyingNumberType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.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 fmt.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 fmt.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
|
||||
}
|
||||
}
|
||||
+517
@@ -0,0 +1,517 @@
|
||||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
case []Float4:
|
||||
if value == nil {
|
||||
*dst = Float4Array{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = Float4Array{Status: Present}
|
||||
} else {
|
||||
*dst = Float4Array{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = Float4Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for Float4Array", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = Float4Array{Status: Present}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to Float4Array", src)
|
||||
}
|
||||
|
||||
*dst = Float4Array{
|
||||
Elements: make([]Float4, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Status: Present,
|
||||
}
|
||||
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
// Maybe the target was one dimension too far, try again:
|
||||
if len(dst.Dimensions) > 1 {
|
||||
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
|
||||
elementsLength = 0
|
||||
for _, dim := range dst.Dimensions {
|
||||
if elementsLength == 0 {
|
||||
elementsLength = int(dim.Length)
|
||||
} else {
|
||||
elementsLength *= int(dim.Length)
|
||||
}
|
||||
}
|
||||
dst.Elements = make([]Float4, elementsLength)
|
||||
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to Float4Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Float4Array) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to Float4Array")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in Float4Array", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, 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:
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
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 *[]*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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (src *Float4Array) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from Float4Array")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from Float4Array")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
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" || uta.Quoted[i] {
|
||||
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, fmt.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 fmt.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
|
||||
}
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Float8 struct {
|
||||
Float float64
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Float8) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
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 fmt.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 fmt.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 fmt.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 fmt.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}
|
||||
case *float64:
|
||||
if value == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *float32:
|
||||
if value == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int8:
|
||||
if value == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint8:
|
||||
if value == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int16:
|
||||
if value == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint16:
|
||||
if value == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int32:
|
||||
if value == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint32:
|
||||
if value == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int64:
|
||||
if value == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint64:
|
||||
if value == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int:
|
||||
if value == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint:
|
||||
if value == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *string:
|
||||
if value == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
default:
|
||||
if originalSrc, ok := underlyingNumberType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.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 fmt.Errorf("invalid length for float8: %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 fmt.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
|
||||
}
|
||||
}
|
||||
+517
@@ -0,0 +1,517 @@
|
||||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
case []Float8:
|
||||
if value == nil {
|
||||
*dst = Float8Array{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = Float8Array{Status: Present}
|
||||
} else {
|
||||
*dst = Float8Array{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = Float8Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for Float8Array", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = Float8Array{Status: Present}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to Float8Array", src)
|
||||
}
|
||||
|
||||
*dst = Float8Array{
|
||||
Elements: make([]Float8, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Status: Present,
|
||||
}
|
||||
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
// Maybe the target was one dimension too far, try again:
|
||||
if len(dst.Dimensions) > 1 {
|
||||
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
|
||||
elementsLength = 0
|
||||
for _, dim := range dst.Dimensions {
|
||||
if elementsLength == 0 {
|
||||
elementsLength = int(dim.Length)
|
||||
} else {
|
||||
elementsLength *= int(dim.Length)
|
||||
}
|
||||
}
|
||||
dst.Elements = make([]Float8, elementsLength)
|
||||
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to Float8Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Float8Array) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to Float8Array")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in Float8Array", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, 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:
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (src *Float8Array) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from Float8Array")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from Float8Array")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
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" || uta.Quoted[i] {
|
||||
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, fmt.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 fmt.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
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
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()
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
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()
|
||||
}
|
||||
+465
@@ -0,0 +1,465 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
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}
|
||||
case map[string]*string:
|
||||
m := make(map[string]Text, len(value))
|
||||
for k, v := range value {
|
||||
if v == nil {
|
||||
m[k] = Text{Status: Null}
|
||||
} else {
|
||||
m[k] = Text{String: *v, Status: Present}
|
||||
}
|
||||
}
|
||||
*dst = Hstore{Map: m, Status: Present}
|
||||
case map[string]Text:
|
||||
*dst = Hstore{Map: value, Status: Present}
|
||||
default:
|
||||
return fmt.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 fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
(*v)[k] = val.String
|
||||
}
|
||||
return nil
|
||||
case *map[string]*string:
|
||||
*v = make(map[string]*string, len(src.Map))
|
||||
for k, val := range src.Map {
|
||||
switch val.Status {
|
||||
case Null:
|
||||
(*v)[k] = nil
|
||||
case Present:
|
||||
str := val.String
|
||||
(*v)[k] = &str
|
||||
default:
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return fmt.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.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 fmt.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 fmt.Errorf("hstore incomplete %v", src)
|
||||
}
|
||||
keyLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
|
||||
rp += 4
|
||||
|
||||
if len(src[rp:]) < keyLen {
|
||||
return fmt.Errorf("hstore incomplete %v", src)
|
||||
}
|
||||
key := string(src[rp : rp+keyLen])
|
||||
rp += keyLen
|
||||
|
||||
if len(src[rp:]) < 4 {
|
||||
return fmt.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
|
||||
|
||||
inElemBuf := make([]byte, 0, 32)
|
||||
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, inElemBuf)
|
||||
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
|
||||
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 = fmt.Errorf("Invalid character '%c' after '=>', expecting '\"' or 'NULL'", r)
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("Invalid character after '=', expecting '>'")
|
||||
}
|
||||
} else {
|
||||
err = fmt.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 = fmt.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 ',', expecting space")
|
||||
case (unicode.IsSpace(r)):
|
||||
r, end = p.Consume()
|
||||
state = hsKey
|
||||
default:
|
||||
err = fmt.Errorf("Invalid character '%c' after ', ', expecting \"", r)
|
||||
}
|
||||
} else {
|
||||
err = fmt.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 fmt.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src Hstore) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
+489
@@ -0,0 +1,489 @@
|
||||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
case []Hstore:
|
||||
if value == nil {
|
||||
*dst = HstoreArray{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = HstoreArray{Status: Present}
|
||||
} else {
|
||||
*dst = HstoreArray{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = HstoreArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for HstoreArray", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = HstoreArray{Status: Present}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to HstoreArray", src)
|
||||
}
|
||||
|
||||
*dst = HstoreArray{
|
||||
Elements: make([]Hstore, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Status: Present,
|
||||
}
|
||||
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
// Maybe the target was one dimension too far, try again:
|
||||
if len(dst.Dimensions) > 1 {
|
||||
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
|
||||
elementsLength = 0
|
||||
for _, dim := range dst.Dimensions {
|
||||
if elementsLength == 0 {
|
||||
elementsLength = int(dim.Length)
|
||||
} else {
|
||||
elementsLength *= int(dim.Length)
|
||||
}
|
||||
}
|
||||
dst.Elements = make([]Hstore, elementsLength)
|
||||
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to HstoreArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *HstoreArray) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to HstoreArray")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in HstoreArray", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, 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:
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (src *HstoreArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from HstoreArray")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from HstoreArray")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
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" || uta.Quoted[i] {
|
||||
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, fmt.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 fmt.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
|
||||
}
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case net.IPNet:
|
||||
*dst = Inet{IPNet: &value, Status: Present}
|
||||
case net.IP:
|
||||
if len(value) == 0 {
|
||||
*dst = Inet{Status: Null}
|
||||
} else {
|
||||
bitCount := len(value) * 8
|
||||
mask := net.CIDRMask(bitCount, bitCount)
|
||||
*dst = Inet{IPNet: &net.IPNet{Mask: mask, IP: value}, Status: Present}
|
||||
}
|
||||
case string:
|
||||
ip, ipnet, err := net.ParseCIDR(value)
|
||||
if err != nil {
|
||||
ip := net.ParseIP(value)
|
||||
if ip == nil {
|
||||
return fmt.Errorf("unable to parse inet address: %s", value)
|
||||
}
|
||||
|
||||
if ipv4 := maybeGetIPv4(value, ip); ipv4 != nil {
|
||||
ipnet = &net.IPNet{IP: ipv4, Mask: net.CIDRMask(32, 32)}
|
||||
} else {
|
||||
ipnet = &net.IPNet{IP: ip, Mask: net.CIDRMask(128, 128)}
|
||||
}
|
||||
} else {
|
||||
ipnet.IP = ip
|
||||
if ipv4 := maybeGetIPv4(value, ipnet.IP); ipv4 != nil {
|
||||
ipnet.IP = ipv4
|
||||
if len(ipnet.Mask) == 16 {
|
||||
ipnet.Mask = ipnet.Mask[12:] // Not sure this is ever needed.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Inet{IPNet: ipnet, Status: Present}
|
||||
case *net.IPNet:
|
||||
if value == nil {
|
||||
*dst = Inet{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *net.IP:
|
||||
if value == nil {
|
||||
*dst = Inet{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *string:
|
||||
if value == nil {
|
||||
*dst = Inet{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
default:
|
||||
if tv, ok := src.(encoding.TextMarshaler); ok {
|
||||
text, err := tv.MarshalText()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot marshal %v: %w", value, err)
|
||||
}
|
||||
return dst.Set(string(text))
|
||||
}
|
||||
if sv, ok := src.(fmt.Stringer); ok {
|
||||
return dst.Set(sv.String())
|
||||
}
|
||||
if originalSrc, ok := underlyingPtrType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to Inet", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert the net.IP to IPv4, if appropriate.
|
||||
//
|
||||
// When parsing a string to a net.IP using net.ParseIP() and the like, we get a
|
||||
// 16 byte slice for IPv4 addresses as well as IPv6 addresses. This function
|
||||
// calls To4() to convert them to a 4 byte slice. This is useful as it allows
|
||||
// users of the net.IP check for IPv4 addresses based on the length and makes
|
||||
// it clear we are handling IPv4 as opposed to IPv6 or IPv4-mapped IPv6
|
||||
// addresses.
|
||||
func maybeGetIPv4(input string, ip net.IP) net.IP {
|
||||
// Do not do this if the provided input looks like IPv6. This is because
|
||||
// To4() on IPv4-mapped IPv6 addresses converts them to IPv4, which behave
|
||||
// different in some cases.
|
||||
if strings.Contains(input, ":") {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ip.To4()
|
||||
}
|
||||
|
||||
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 fmt.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 tv, ok := dst.(encoding.TextUnmarshaler); ok {
|
||||
if err := tv.UnmarshalText([]byte(src.IPNet.String())); err != nil {
|
||||
return fmt.Errorf("cannot unmarshal %v to %T: %w", src, dst, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return fmt.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.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 {
|
||||
if ipv4 := ip.To4(); ipv4 != nil {
|
||||
ip = ipv4
|
||||
}
|
||||
bitCount := len(ip) * 8
|
||||
mask := net.CIDRMask(bitCount, bitCount)
|
||||
ipnet = &net.IPNet{Mask: mask, IP: ip}
|
||||
} else {
|
||||
ip, ipnet, err = net.ParseCIDR(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ipv4 := ip.To4(); ipv4 != nil {
|
||||
ip = ipv4
|
||||
}
|
||||
ones, _ := ipnet.Mask.Size()
|
||||
*ipnet = net.IPNet{IP: ip, Mask: net.CIDRMask(ones, len(ip)*8)}
|
||||
}
|
||||
|
||||
*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 fmt.Errorf("Received an invalid size for an 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:])
|
||||
if ipv4 := ipnet.IP.To4(); ipv4 != nil {
|
||||
ipnet.IP = ipv4
|
||||
}
|
||||
ipnet.Mask = net.CIDRMask(int(bits), len(ipnet.IP)*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, fmt.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 fmt.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src Inet) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
+546
@@ -0,0 +1,546 @@
|
||||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
case []Inet:
|
||||
if value == nil {
|
||||
*dst = InetArray{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = InetArray{Status: Present}
|
||||
} else {
|
||||
*dst = InetArray{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = InetArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for InetArray", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = InetArray{Status: Present}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to InetArray", src)
|
||||
}
|
||||
|
||||
*dst = InetArray{
|
||||
Elements: make([]Inet, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Status: Present,
|
||||
}
|
||||
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
// Maybe the target was one dimension too far, try again:
|
||||
if len(dst.Dimensions) > 1 {
|
||||
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
|
||||
elementsLength = 0
|
||||
for _, dim := range dst.Dimensions {
|
||||
if elementsLength == 0 {
|
||||
elementsLength = int(dim.Length)
|
||||
} else {
|
||||
elementsLength *= int(dim.Length)
|
||||
}
|
||||
}
|
||||
dst.Elements = make([]Inet, elementsLength)
|
||||
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to InetArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *InetArray) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to InetArray")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in InetArray", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, 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:
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (src *InetArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from InetArray")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from InetArray")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
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" || uta.Quoted[i] {
|
||||
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, fmt.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 fmt.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
|
||||
}
|
||||
+321
@@ -0,0 +1,321 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Int2 struct {
|
||||
Int int16
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int2) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
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 fmt.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case int32:
|
||||
if value < math.MinInt16 {
|
||||
return fmt.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
if value > math.MaxInt16 {
|
||||
return fmt.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case uint32:
|
||||
if value > math.MaxInt16 {
|
||||
return fmt.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case int64:
|
||||
if value < math.MinInt16 {
|
||||
return fmt.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
if value > math.MaxInt16 {
|
||||
return fmt.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case uint64:
|
||||
if value > math.MaxInt16 {
|
||||
return fmt.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case int:
|
||||
if value < math.MinInt16 {
|
||||
return fmt.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
if value > math.MaxInt16 {
|
||||
return fmt.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case uint:
|
||||
if value > math.MaxInt16 {
|
||||
return fmt.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}
|
||||
case float32:
|
||||
if value > math.MaxInt16 {
|
||||
return fmt.Errorf("%f is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case float64:
|
||||
if value > math.MaxInt16 {
|
||||
return fmt.Errorf("%f is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case *int8:
|
||||
if value == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint8:
|
||||
if value == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int16:
|
||||
if value == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint16:
|
||||
if value == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int32:
|
||||
if value == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint32:
|
||||
if value == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int64:
|
||||
if value == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint64:
|
||||
if value == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int:
|
||||
if value == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint:
|
||||
if value == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *string:
|
||||
if value == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *float32:
|
||||
if value == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *float64:
|
||||
if value == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
default:
|
||||
if originalSrc, ok := underlyingNumberType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.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 fmt.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 fmt.Errorf("%d is greater than maximum value for Int2", src)
|
||||
}
|
||||
if src > math.MaxInt16 {
|
||||
return fmt.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 fmt.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
|
||||
}
|
||||
|
||||
func (dst *Int2) UnmarshalJSON(b []byte) error {
|
||||
var n *int16
|
||||
err := json.Unmarshal(b, &n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
} else {
|
||||
*dst = Int2{Int: *n, Status: Present}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+909
@@ -0,0 +1,909 @@
|
||||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
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 []*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,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
case []int32:
|
||||
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 []*int32:
|
||||
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 []uint32:
|
||||
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 []*uint32:
|
||||
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 []int64:
|
||||
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 []*int64:
|
||||
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 []uint64:
|
||||
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 []*uint64:
|
||||
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 []int:
|
||||
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 []*int:
|
||||
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 []uint:
|
||||
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 []*uint:
|
||||
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 []Int2:
|
||||
if value == nil {
|
||||
*dst = Int2Array{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = Int2Array{Status: Present}
|
||||
} else {
|
||||
*dst = Int2Array{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = Int2Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for Int2Array", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = Int2Array{Status: Present}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to Int2Array", src)
|
||||
}
|
||||
|
||||
*dst = Int2Array{
|
||||
Elements: make([]Int2, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Status: Present,
|
||||
}
|
||||
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
// Maybe the target was one dimension too far, try again:
|
||||
if len(dst.Dimensions) > 1 {
|
||||
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
|
||||
elementsLength = 0
|
||||
for _, dim := range dst.Dimensions {
|
||||
if elementsLength == 0 {
|
||||
elementsLength = int(dim.Length)
|
||||
} else {
|
||||
elementsLength *= int(dim.Length)
|
||||
}
|
||||
}
|
||||
dst.Elements = make([]Int2, elementsLength)
|
||||
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to Int2Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int2Array) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to Int2Array")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in Int2Array", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, 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:
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
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 *[]*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
|
||||
|
||||
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
|
||||
|
||||
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 *[]*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
|
||||
|
||||
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
|
||||
|
||||
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 *[]*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
|
||||
|
||||
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
|
||||
|
||||
case *[]int:
|
||||
*v = make([]int, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *[]*int:
|
||||
*v = make([]*int, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *[]uint:
|
||||
*v = make([]uint, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *[]*uint:
|
||||
*v = make([]*uint, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (src *Int2Array) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from Int2Array")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from Int2Array")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
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" || uta.Quoted[i] {
|
||||
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, fmt.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 fmt.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
|
||||
}
|
||||
+312
@@ -0,0 +1,312 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Int4 struct {
|
||||
Int int32
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int4) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
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 fmt.Errorf("%d is greater than maximum value for Int4", value)
|
||||
}
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case int64:
|
||||
if value < math.MinInt32 {
|
||||
return fmt.Errorf("%d is greater than maximum value for Int4", value)
|
||||
}
|
||||
if value > math.MaxInt32 {
|
||||
return fmt.Errorf("%d is greater than maximum value for Int4", value)
|
||||
}
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case uint64:
|
||||
if value > math.MaxInt32 {
|
||||
return fmt.Errorf("%d is greater than maximum value for Int4", value)
|
||||
}
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case int:
|
||||
if value < math.MinInt32 {
|
||||
return fmt.Errorf("%d is greater than maximum value for Int4", value)
|
||||
}
|
||||
if value > math.MaxInt32 {
|
||||
return fmt.Errorf("%d is greater than maximum value for Int4", value)
|
||||
}
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case uint:
|
||||
if value > math.MaxInt32 {
|
||||
return fmt.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}
|
||||
case float32:
|
||||
if value > math.MaxInt32 {
|
||||
return fmt.Errorf("%f is greater than maximum value for Int4", value)
|
||||
}
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case float64:
|
||||
if value > math.MaxInt32 {
|
||||
return fmt.Errorf("%f is greater than maximum value for Int4", value)
|
||||
}
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case *int8:
|
||||
if value == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint8:
|
||||
if value == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int16:
|
||||
if value == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint16:
|
||||
if value == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int32:
|
||||
if value == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint32:
|
||||
if value == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int64:
|
||||
if value == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint64:
|
||||
if value == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *int:
|
||||
if value == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *uint:
|
||||
if value == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *string:
|
||||
if value == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *float32:
|
||||
if value == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
case *float64:
|
||||
if value == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
return dst.Set(*value)
|
||||
}
|
||||
default:
|
||||
if originalSrc, ok := underlyingNumberType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.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 fmt.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 fmt.Errorf("%d is greater than maximum value for Int4", src)
|
||||
}
|
||||
if src > math.MaxInt32 {
|
||||
return fmt.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 fmt.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
|
||||
}
|
||||
|
||||
if n == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
} else {
|
||||
*dst = Int4{Int: *n, Status: Present}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+909
@@ -0,0 +1,909 @@
|
||||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
switch value := src.(type) {
|
||||
|
||||
case []int16:
|
||||
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 []*int16:
|
||||
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 []uint16:
|
||||
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 []*uint16:
|
||||
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 []*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,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
case []int64:
|
||||
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 []*int64:
|
||||
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 []uint64:
|
||||
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 []*uint64:
|
||||
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 []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 []*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 []uint:
|
||||
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 []*uint:
|
||||
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 []Int4:
|
||||
if value == nil {
|
||||
*dst = Int4Array{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = Int4Array{Status: Present}
|
||||
} else {
|
||||
*dst = Int4Array{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = Int4Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for Int4Array", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = Int4Array{Status: Present}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to Int4Array", src)
|
||||
}
|
||||
|
||||
*dst = Int4Array{
|
||||
Elements: make([]Int4, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Status: Present,
|
||||
}
|
||||
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
// Maybe the target was one dimension too far, try again:
|
||||
if len(dst.Dimensions) > 1 {
|
||||
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
|
||||
elementsLength = 0
|
||||
for _, dim := range dst.Dimensions {
|
||||
if elementsLength == 0 {
|
||||
elementsLength = int(dim.Length)
|
||||
} else {
|
||||
elementsLength *= int(dim.Length)
|
||||
}
|
||||
}
|
||||
dst.Elements = make([]Int4, elementsLength)
|
||||
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to Int4Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int4Array) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to Int4Array")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in Int4Array", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, 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:
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
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 *[]*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
|
||||
|
||||
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
|
||||
|
||||
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 *[]*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
|
||||
|
||||
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
|
||||
|
||||
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 *[]*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
|
||||
|
||||
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
|
||||
|
||||
case *[]int:
|
||||
*v = make([]int, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *[]*int:
|
||||
*v = make([]*int, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *[]uint:
|
||||
*v = make([]uint, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *[]*uint:
|
||||
*v = make([]*uint, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (src *Int4Array) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from Int4Array")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from Int4Array")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
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" || uta.Quoted[i] {
|
||||
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, fmt.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 fmt.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
|
||||
}
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Int4multirange struct {
|
||||
Ranges []Int4range
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int4multirange) Set(src interface{}) error {
|
||||
//untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = Int4multirange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case Int4multirange:
|
||||
*dst = value
|
||||
case *Int4multirange:
|
||||
*dst = *value
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(value))
|
||||
case []Int4range:
|
||||
if value == nil {
|
||||
*dst = Int4multirange{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = Int4multirange{Status: Present}
|
||||
} else {
|
||||
elements := make([]Int4range, len(value))
|
||||
for i := range value {
|
||||
if err := elements[i].Set(value[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*dst = Int4multirange{
|
||||
Ranges: elements,
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
case []*Int4range:
|
||||
if value == nil {
|
||||
*dst = Int4multirange{Status: Null}
|
||||
} else if len(value) == 0 {
|
||||
*dst = Int4multirange{Status: Present}
|
||||
} else {
|
||||
elements := make([]Int4range, len(value))
|
||||
for i := range value {
|
||||
if err := elements[i].Set(value[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*dst = Int4multirange{
|
||||
Ranges: elements,
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("cannot convert %v to Int4multirange", src)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (dst Int4multirange) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int4multirange) AssignTo(dst interface{}) error {
|
||||
return fmt.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Int4multirange) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int4multirange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
utmr, err := ParseUntypedTextMultirange(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Int4range
|
||||
|
||||
if len(utmr.Elements) > 0 {
|
||||
elements = make([]Int4range, len(utmr.Elements))
|
||||
|
||||
for i, s := range utmr.Elements {
|
||||
var elem Int4range
|
||||
|
||||
elemSrc := []byte(s)
|
||||
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Int4multirange{Ranges: elements, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int4multirange) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int4multirange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
rp := 0
|
||||
|
||||
numElems := int(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
|
||||
if numElems == 0 {
|
||||
*dst = Int4multirange{Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elements := make([]Int4range, numElems)
|
||||
|
||||
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 = Int4multirange{Ranges: elements, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src Int4multirange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = append(buf, '{')
|
||||
|
||||
inElemBuf := make([]byte, 0, 32)
|
||||
for i, elem := range src.Ranges {
|
||||
if i > 0 {
|
||||
buf = append(buf, ',')
|
||||
}
|
||||
|
||||
elemBuf, err := elem.EncodeText(ci, inElemBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf == nil {
|
||||
return nil, fmt.Errorf("multi-range does not allow null range")
|
||||
} else {
|
||||
buf = append(buf, string(elemBuf)...)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
buf = append(buf, '}')
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src Int4multirange) 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.Ranges)))
|
||||
|
||||
for i := range src.Ranges {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
elemBuf, err := src.Ranges[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 *Int4multirange) 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 fmt.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src Int4multirange) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type Int4range struct {
|
||||
Lower Int4
|
||||
Upper Int4
|
||||
LowerType BoundType
|
||||
UpperType BoundType
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int4range) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = Int4range{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case Int4range:
|
||||
*dst = value
|
||||
case *Int4range:
|
||||
*dst = *value
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(value))
|
||||
default:
|
||||
return fmt.Errorf("cannot convert %v to Int4range", src)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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 fmt.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, fmt.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, fmt.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, fmt.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, fmt.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, fmt.Errorf("unknown LowerType: %v", src.LowerType)
|
||||
}
|
||||
|
||||
switch src.UpperType {
|
||||
case Inclusive:
|
||||
rangeType |= upperInclusiveMask
|
||||
case Unbounded:
|
||||
rangeType |= upperUnboundedMask
|
||||
case Exclusive:
|
||||
default:
|
||||
return nil, fmt.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, fmt.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, fmt.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 fmt.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src Int4range) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user