golang pgx自定义PostgreSQL类型

简介: golang的pgx驱动提供了大约70种PostgreSQL类型支持,但还是有一些类型没有涵盖,本文介绍如何自己编写代码支持特殊的类型。

golang的pgx驱动提供了大约70种PostgreSQL类型支持,但还是有一些类型没有涵盖,本文介绍如何自己编写代码支持特殊的类型。本文以PostGIS的Geometry类型为例。

需要实现:SetGetAssignToDecodeTextDecodeBinaryEncodeTextEncodeBinaryScanValueMarshalJSONUnmarshalJSON方法。

//PostGIS Geometry实现,目前仅支持Point2Dimport (
"bytes""database/sql/driver""encoding/binary""encoding/hex""errors""fmt""github.com/jackc/pgtype""math""strconv""strings")
const (
TypeGeoPoint=iota+1TypeGeoPointMTypeGeoPointZ)
typeGeometrystruct {
Xfloat64Yfloat64Statuspgtype.Status}
funcAppendByte(buf []byte, nbyte) []byte {
buf=append(buf, n)
returnbuf}
funcAppendUint16(buf []byte, nuint16) []byte {
wp :=len(buf)
buf=append(buf, 0, 0)
binary.LittleEndian.PutUint16(buf[wp:], n)
returnbuf}
funcAppendUint32(buf []byte, nuint32) []byte {
wp :=len(buf)
buf=append(buf, 0, 0, 0, 0)
binary.LittleEndian.PutUint32(buf[wp:], n)
returnbuf}
funcAppendUint64(buf []byte, nuint64) []byte {
wp :=len(buf)
buf=append(buf, 0, 0, 0, 0, 0, 0, 0, 0)
binary.LittleEndian.PutUint64(buf[wp:], n)
returnbuf}
funcAppendInt16(buf []byte, nint16) []byte {
returnAppendUint16(buf, uint16(n))
}
funcAppendInt32(buf []byte, nint32) []byte {
returnAppendUint32(buf, uint32(n))
}
funcAppendInt64(buf []byte, nint64) []byte {
returnAppendUint64(buf, uint64(n))
}
funcSetInt32(buf []byte, nint32) {
binary.LittleEndian.PutUint32(buf, uint32(n))
}
func (dst*Geometry) Set(srcinterface{}) error {
ifsrc==nil {
dst.Status=pgtype.Nullreturnnil    }
err :=fmt.Errorf("cannot convert %v to Geometry", src)
varp*Geometryswitchvalue :=src.(type) {
casestring:
p, err=parseGeometry([]byte(value))
case []byte:
p, err=parseGeometry(value)
default:
returnerr    }
iferr!=nil {
returnerr    }
*dst=*preturnnil}
funcparseGeometry(src []byte) (*Geometry, error) {
ifsrc==nil||bytes.Compare(src, []byte("null")) ==0 {
return&Geometry{Status: pgtype.Null}, nil    }
iflen(src) <5 {
returnnil, fmt.Errorf("invalid length for point: %v", len(src))
    }
ifsrc[0] =='"'&&src[len(src)-1] =='"' {
src=src[1 : len(src)-1]
    }
parts :=strings.SplitN(string(src[0:len(src)-1]), ",", 2)
iflen(parts) <2 {
returnnil, fmt.Errorf("invalid format for point")
    }
x, err :=strconv.ParseFloat(parts[0], 64)
iferr!=nil {
returnnil, err    }
y, err :=strconv.ParseFloat(parts[1], 64)
iferr!=nil {
returnnil, err    }
return&Geometry{X: x, Y: y, Status: pgtype.Present}, nil}
func (dstGeometry) Get() interface{} {
switchdst.Status {
casepgtype.Present:
returndstcasepgtype.Null:
returnnildefault:
returndst.Status    }
}
func (src*Geometry) AssignTo(dstinterface{}) error {
returnfmt.Errorf("cannot assign %v to %T", src, dst)
}
func (dst*Geometry) DecodeText(ci*pgtype.ConnInfo, src []byte) error {
ifsrc==nil {
*dst=Geometry{Status: pgtype.Null}
returnnil    }
s, _ :=hex.DecodeString(string(src))
l :=len(s)
begin :=l-2*8//只取坐标值parts :=s[begin:]
iflen(parts) !=16 {
returnfmt.Errorf("invalid format for geometry")
    }
x :=binary.LittleEndian.Uint64(parts)
y :=binary.LittleEndian.Uint64(parts[8:])
*dst=Geometry{X: math.Float64frombits(x), Y: math.Float64frombits(y), Status: pgtype.Present}
//*dst = Geometry{X: x, Y: y, Status: pgtype.Present}returnnil}
func (dst*Geometry) DecodeBinary(ci*pgtype.ConnInfo, src []byte) error {
ifsrc==nil {
*dst=Geometry{Status: pgtype.Null}
returnnil    }
l :=len(src)
begin :=l-2*8parts :=src[begin:]
iflen(parts) !=16 {
returnfmt.Errorf("invalid length for geometry: %v", len(src))
    }
x :=binary.LittleEndian.Uint64(parts)
y :=binary.LittleEndian.Uint64(parts[8:])
*dst=Geometry{
X:      math.Float64frombits(x),
Y:      math.Float64frombits(y),
Status: pgtype.Present,
    }
returnnil}
func (srcGeometry) EncodeText(ci*pgtype.ConnInfo, buf []byte) ([]byte, error) {
switchsrc.Status {
casepgtype.Null:
returnnil, nilcasepgtype.Undefined:
returnnil, errors.New("cannot encode status undefined")
    }
buf=AppendByte(buf, 0x01)
//1-不带SRID, 0x20000000 - 带SRID//坐标类型 point(1)buf=AppendInt32(buf, 0x20000000|1)
//WGS 84 SRID=4326buf=AppendInt32(buf, 0x10E6)
buf=AppendUint64(buf, math.Float64bits(src.X))
buf=AppendUint64(buf, math.Float64bits(src.Y))
s :=hex.EncodeToString(buf)
return []byte(s), nil}
func (srcGeometry) EncodeBinary(ci*pgtype.ConnInfo, buf []byte) ([]byte, error) {
switchsrc.Status {
casepgtype.Null:
returnnil, nilcasepgtype.Undefined:
returnnil, errors.New("cannot encode status undefined")
    }
buf=AppendByte(buf, 0x01)
//1-不带SRID, 0x20000000 - 带SRID//坐标类型 point(1)buf=AppendInt32(buf, 0x20000000|1)
//WGS 84 SRID=4326buf=AppendInt32(buf, 0x10E6)
buf=AppendUint64(buf, math.Float64bits(src.X))
buf=AppendUint64(buf, math.Float64bits(src.Y))
returnbuf, nil}
// Scan implements the database/sql Scanner interface.func (dst*Geometry) Scan(srcinterface{}) error {
ifsrc==nil {
*dst=Geometry{Status: pgtype.Null}
returnnil    }
switchsrc :=src.(type) {
casestring:
returndst.DecodeText(nil, []byte(src))
case []byte:
srcCopy :=make([]byte, len(src))
copy(srcCopy, src)
returndst.DecodeText(nil, srcCopy)
    }
returnfmt.Errorf("cannot scan %T", src)
}
// Value implements the database/sql/driver Valuer interface.func (srcGeometry) Value() (driver.Value, error) {
returnpgtype.EncodeValueText(src)
}
func (srcGeometry) MarshalJSON() ([]byte, error) {
switchsrc.Status {
casepgtype.Present:
varbuffbytes.Bufferbuff.WriteByte('"')
buff.WriteString(fmt.Sprintf("(%g,%g)", src.X, src.Y))
buff.WriteByte('"')
returnbuff.Bytes(), nilcasepgtype.Null:
return []byte("null"), nilcasepgtype.Undefined:
returnnil, errors.New("cannot encode status undefined")
    }
returnnil, errors.New("invalid status")
}
func (dst*Geometry) UnmarshalJSON(geometry []byte) error {
p, err :=parseGeometry(geometry)
iferr!=nil {
returnerr    }
*dst=*preturnnil}

使用前,需要注册自定义的类型,在程序的初始化部分加上以下代码:

urlExample :="postgres://username:password@localhost:5432/database_name"vargeometryOiduint32config, err :=pgxpool.ParseConfig(urlExample)
iferr!=nil {
log.Panic("parse database config failed", zap.Error(err))
}
config.AfterConnect=func(ctxcontext.Context, conn*pgx.Conn) error {
//注册PostGIS geometry类型ifgeometryOid==0 {
//取得geometry类型的OIDerr=conn.QueryRow(ctx, "select 'geometry'::regtype::oid").Scan(&geometryOid)
iferr!=nil {
log.Panic("get geometry oid failed", zap.Error(err))
        }
    }
ci :=conn.ConnInfo()
ci.RegisterDataType(pgtype.DataType{Value: &mypgtype.Geometry{}, Name: "geometry", OID: geometryOid})
returnnil}
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
存储 Go
Golang底层原理剖析之slice类型与扩容机制
Golang底层原理剖析之slice类型与扩容机制
196 0
|
2月前
|
Java 编译器 Go
【Golang】(5)Go基础的进阶知识!带你认识迭代器与类型以及声明并使用接口与泛型!
好烦好烦好烦!你是否还在为弄不懂Go中的泛型和接口而烦恼?是否还在苦恼思考迭代器的运行方式和意义?本篇文章将带你了解Go的接口与泛型,还有迭代器的使用,附送类型断言的解释
184 3
|
2月前
|
存储 Java Go
【Golang】(3)条件判断与循环?切片和数组的关系?映射表与Map?三组关系傻傻分不清?本文带你了解基本的复杂类型与执行判断语句
在Go中,条件控制语句总共有三种if、switch、select。循环只有for,不过for可以充当while使用。如果想要了解这些知识点,初学者进入文章中来感受吧!
148 1
|
3月前
|
Linux Go iOS开发
IDA 9.2 发布:Golang 改进、新 UI 组件、类型解析等
IDA Pro 9.2 (macOS, Linux, Windows) - 强大的反汇编程序、反编译器和多功能调试器
729 0
|
8月前
|
JSON API Go
Golang工程组件:自定义HTTP规则的grpc-gateway选项
总的来说,grpc-gateway提供了一种简单有效的方式来为你的gRPC服务提供RESTful风格的API。通过自定义HTTP规则,你可以灵活地定义你的API的行为,以满足你的应用的需求。
209 27
|
SQL 关系型数据库 数据库
实时计算 Flink版操作报错之使用SQL 将 PostgreSQL 的 date 类型字段转换为 TIMESTAMP 类型时遇到报错,该如何处理
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
Go
Golang语言基础之接口(interface)及类型断言
这篇文章是关于Go语言中接口(interface)及类型断言的详细教程,涵盖了接口的概念、定义、实现、使用注意事项以及类型断言的多种场景和方法。
446 4
|
Go
Golang语言基础数据类型之字符类型
这篇文章介绍了Go语言中的字符类型,包括字符概述、byte和rune类型的定义、转义字符的使用以及如何遍历字符串获取字符的示例。
162 0
|
JSON 安全 Go
[golang]使用logrus自定义日志模块
[golang]使用logrus自定义日志模块
283 0
|
分布式计算 DataWorks 关系型数据库
DataWorks产品使用合集之在使用 DataWorks 数据集成同步 PostgreSQL 数据库中的 Geometry 类型数据如何解决
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
257 0

推荐镜像

更多