Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

*: Geospatial support #38611

Draft
wants to merge 35 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4d7fcf3
WIP: geospatial
dveeden Oct 19, 2022
37acd44
Fix ST_Intersects() to work on typs other than just points
dveeden Oct 21, 2022
ff673e2
Small fixes
dveeden Oct 23, 2022
dc25b35
stGeomFromTextFunctionClass: check number of arguments
dveeden Oct 23, 2022
ce4d9eb
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Oct 23, 2022
98d46ad
Fix bazel build
dveeden Oct 23, 2022
e8e886a
Update nr of builtin functions in TestShowBuiltin
dveeden Oct 23, 2022
99e4844
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Oct 24, 2022
9bc8cc0
Add test for geospatial functions
dveeden Oct 24, 2022
a3e6e44
Add test for ST_AsText()
dveeden Oct 24, 2022
a12a988
Merge branch 'master' into geospatial
dveeden Oct 25, 2022
17f3a94
Remove ST_Intersects()
dveeden Oct 25, 2022
0acbfff
Add test for ST_Distance()
dveeden Oct 25, 2022
cefcc41
Update nr of builtin functions
dveeden Oct 25, 2022
0508b90
Add more comments
dveeden Oct 25, 2022
f9d2af1
Add builtin_geo_test.go to BUILD.bazel
dveeden Oct 25, 2022
480137d
Merge branch 'master' into geospatial
dveeden Oct 26, 2022
4088ea4
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Nov 7, 2022
817a994
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Nov 13, 2022
00b573a
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Nov 14, 2022
f72f7e5
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Nov 21, 2022
e720ab2
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Nov 21, 2022
b1ce488
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Nov 24, 2022
1bcc32d
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Dec 5, 2022
e2c7f4c
Add SRID support
dveeden Dec 7, 2022
7e1a55c
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Dec 7, 2022
2b488c3
Fix location of SRID in show output
dveeden Dec 13, 2022
e9414ff
Check SRID on insert
dveeden Dec 13, 2022
0cc5f3f
Add POINT type
dveeden Dec 27, 2022
b1f3ed7
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Dec 27, 2022
764ed33
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Jan 19, 2023
75badb3
Fix SRID comment
dveeden Jan 19, 2023
d82d83b
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Mar 28, 2023
ae6f0fc
Update go mods
dveeden Mar 29, 2023
a349b45
Merge remote-tracking branch 'upstream/master' into geospatial
dveeden Mar 31, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions DEPS.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -4192,6 +4192,21 @@ def go_deps():
sum = "h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg=",
version = "v1.1.6",
)
go_repository(
name = "com_github_twpayne_go_geom",
build_file_proto_mode = "disable",
importpath = "github.com/twpayne/go-geom",
sum = "h1:lVzRztQgP4GvJ+VuVDv9tICZIfGFtB9uQQQa7w72Wdk=",
version = "v1.4.3",
)
go_repository(
name = "com_github_twpayne_go_kml",
build_file_proto_mode = "disable",
importpath = "github.com/twpayne/go-kml",
sum = "h1:rFMw2/EwgkVssGS2MT6YfWSPZz6BgcJkLxQ53jnE8rQ=",
version = "v1.5.2",
)

go_repository(
name = "com_github_uber_jaeger_client_go",
build_file_proto_mode = "disable_global",
Expand Down
3 changes: 3 additions & 0 deletions ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1159,6 +1159,9 @@ func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, o
ctx.GetSessionVars().StmtCtx.AppendWarning(dbterror.ErrTableCantHandleFt.GenWithStackByArgs())
case ast.ColumnOptionCheck:
ctx.GetSessionVars().StmtCtx.AppendWarning(dbterror.ErrUnsupportedConstraintCheck.GenWithStackByArgs("CONSTRAINT CHECK"))
case ast.ColumnOptionSrid:
col.AddFlag(mysql.SridFlag)
col.Srid = v.Srid
}
}
}
Expand Down
1 change: 1 addition & 0 deletions errno/errcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,7 @@ const (
ErrCTEMaxRecursionDepth = 3636
ErrNotHintUpdatable = 3637
ErrExistsInHistoryPassword = 3638
ErrWrongSridForColumn = 3643
ErrForeignKeyCannotDropParent = 3730
ErrForeignKeyCannotUseVirtualColumn = 3733
ErrForeignKeyNoColumnInParent = 3734
Expand Down
1 change: 1 addition & 0 deletions errno/errname.go
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,7 @@ var MySQLErrName = map[uint16]*mysql.ErrMessage{
ErrLockAcquireFailAndNoWaitSet: mysql.Message("Statement aborted because lock(s) could not be acquired immediately and NOWAIT is set.", nil),
ErrNotHintUpdatable: mysql.Message("Variable '%s' cannot be set using SET_VAR hint.", nil),
ErrExistsInHistoryPassword: mysql.Message("Cannot use these credentials for '%s@%s' because they contradict the password history policy.", nil),
ErrWrongSridForColumn: mysql.Message("The SRID of the geometry does not match the SRID of the column '%s'. The SRID of the geometry is %d, but the SRID of the column is %d. Consider changing the SRID of the geometry or the SRID property of the column.", nil),
ErrForeignKeyCannotDropParent: mysql.Message("Cannot drop table '%s' referenced by a foreign key constraint '%s' on table '%s'.", nil),
ErrForeignKeyCannotUseVirtualColumn: mysql.Message("Foreign key '%s' uses virtual column '%s' which is not supported.", nil),
ErrForeignKeyNoColumnInParent: mysql.Message("Failed to add the foreign key constraint. Missing column '%s' for constraint '%s' in the referenced table '%s'", nil),
Expand Down
3 changes: 3 additions & 0 deletions executor/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,9 @@ func ConstructResultOfShowCreateTable(ctx sessionctx.Context, tableInfo *model.T
buf.WriteString(" VIRTUAL")
}
}
if mysql.HasSridFlag(col.GetFlag()) {
buf.WriteString(fmt.Sprintf(" /*!80003 SRID %d */", col.Srid))
}
if mysql.HasAutoIncrementFlag(col.GetFlag()) {
hasAutoIncID = true
buf.WriteString(" NOT NULL AUTO_INCREMENT")
Expand Down
6 changes: 6 additions & 0 deletions expression/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ go_library(
"builtin_encryption.go",
"builtin_encryption_vec.go",
"builtin_func_param.go",
"builtin_geo.go",
"builtin_ilike.go",
"builtin_ilike_vec.go",
"builtin_info.go",
Expand Down Expand Up @@ -118,6 +119,10 @@ go_library(
"@com_github_pingcap_tipb//go-tipb",
"@com_github_pkg_errors//:errors",
"@com_github_tikv_client_go_v2//oracle",
"@com_github_twpayne_go_geom//:go-geom",
"@com_github_twpayne_go_geom//encoding/wkb",
"@com_github_twpayne_go_geom//encoding/wkt",
"@com_github_twpayne_go_geom//xy",
"@org_golang_x_exp//slices",
"@org_golang_x_tools//container/intsets",
"@org_uber_go_atomic//:atomic",
Expand All @@ -142,6 +147,7 @@ go_test(
"builtin_control_vec_generated_test.go",
"builtin_encryption_test.go",
"builtin_encryption_vec_test.go",
"builtin_geo_test.go",
"builtin_ilike_test.go",
"builtin_info_test.go",
"builtin_info_vec_test.go",
Expand Down
5 changes: 5 additions & 0 deletions expression/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,11 @@ var funcs = map[string]functionClass{
ast.NextVal: &nextValFunctionClass{baseFunctionClass{ast.NextVal, 1, 1}},
ast.LastVal: &lastValFunctionClass{baseFunctionClass{ast.LastVal, 1, 1}},
ast.SetVal: &setValFunctionClass{baseFunctionClass{ast.SetVal, 2, 2}},

// Geospatial function.
ast.StGeomFromText: &stGeomFromTextFunctionClass{baseFunctionClass{ast.StGeomFromText, 1, -1}},
ast.StAsText: &stAsTextFunctionClass{baseFunctionClass{ast.StAsText, 1, 1}},
ast.StDistance: &stDistanceFunctionClass{baseFunctionClass{ast.StDistance, 1, 2}},
}

// IsFunctionSupported check if given function name is a builtin sql function.
Expand Down
208 changes: 208 additions & 0 deletions expression/builtin_geo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// Copyright 2022 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package expression

import (
"bytes"
"encoding/binary"

"github.com/pingcap/errors"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/chunk"
"github.com/twpayne/go-geom"
"github.com/twpayne/go-geom/encoding/wkb"
"github.com/twpayne/go-geom/encoding/wkt"
"github.com/twpayne/go-geom/xy"
)

var (
_ functionClass = &stAsTextFunctionClass{}
_ functionClass = &stDistanceFunctionClass{}
_ functionClass = &stGeomFromTextFunctionClass{}
)

var (
_ builtinFunc = &builtinStAsTextSig{}
_ builtinFunc = &builtinStDistanceSig{}
_ builtinFunc = &builtinStGeomFromTextSig{}
)

type stGeomFromTextFunctionClass struct {
baseFunctionClass
}

func (c *stGeomFromTextFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
if len(args) > 2 {
return nil, ErrIncorrectParameterCount.GenWithStackByArgs(c.funcName)
}
argTps := make([]types.EvalType, 0, len(args))
for i := 0; i < len(args); i++ {
argTps = append(argTps, types.ETString)
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, argTps...)
if err != nil {
return nil, err
}
types.SetBinChsClnFlag(bf.tp)
sig := &builtinStGeomFromTextSig{bf}
return sig, nil
}

type builtinStGeomFromTextSig struct {
baseBuiltinFunc
}

func (b *builtinStGeomFromTextSig) Clone() builtinFunc {
newSig := &builtinStGeomFromTextSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}

// ST_GeomFromText(wkt) -> geom.
// geom = <encoded_srid><wkb>, Compatible with MySQL 8.0
func (b *builtinStGeomFromTextSig) evalString(row chunk.Row) (string, bool, error) {
wktVal, isNull, err := b.args[0].EvalString(b.ctx, row)
if isNull || err != nil {
return "", isNull, err
}
var srid int64 = 0
if len(b.args) == 2 {
srid, _, err = b.args[1].EvalInt(b.ctx, row)
if err != nil {
return "", isNull, err
}
}
g, err := wkt.Unmarshal(wktVal)
if err != nil {
return "", isNull, err
}
wkbVal, err := wkb.Marshal(g, binary.LittleEndian)
if err != nil {
return "", isNull, err
}
buf := new(bytes.Buffer)
err = binary.Write(buf, binary.LittleEndian, int32(srid))
if err != nil {
return "", isNull, err
}
return string(append(buf.Bytes(), wkbVal...)), false, nil
}

type stAsTextFunctionClass struct {
baseFunctionClass
}

func (c *stAsTextFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETString)
if err != nil {
return nil, err
}
sig := &builtinStAsTextSig{bf}
return sig, nil
}

type builtinStAsTextSig struct {
baseBuiltinFunc
}

func (b *builtinStAsTextSig) Clone() builtinFunc {
newSig := &builtinStAsTextSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}

// ST_AsText(geom) -> wkt
func (b *builtinStAsTextSig) evalString(row chunk.Row) (string, bool, error) {
wkbValRaw, isNull, err := b.args[0].EvalString(b.ctx, row)
if isNull || err != nil {
return "", isNull, err
}
wkbVal := wkbValRaw[4:]

g, err := wkb.Unmarshal([]byte(wkbVal))
if err != nil {
return "", isNull, err
}
wktVal, err := wkt.Marshal(g)
if err != nil {
return "", isNull, err
}
return wktVal, false, nil
}

type stDistanceFunctionClass struct {
baseFunctionClass
}

func (c *stDistanceFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETReal, types.ETString, types.ETString)
if err != nil {
return nil, err
}
sig := &builtinStDistanceSig{bf}
return sig, nil
}

type builtinStDistanceSig struct {
baseBuiltinFunc
}

func (b *builtinStDistanceSig) Clone() builtinFunc {
newSig := &builtinStDistanceSig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}

// ST_Distance(geom, geom) -> distance
// Only supports SRID 0 for now, result is in degrees
func (b *builtinStDistanceSig) evalReal(row chunk.Row) (float64, bool, error) {
g1r, isNull, err := b.args[0].EvalString(b.ctx, row)
if isNull || err != nil {
return 0, isNull, err
}
g2r, _, err := b.args[1].EvalString(b.ctx, row)
if isNull || err != nil {
return 0, isNull, err
}
g1, err := wkb.Unmarshal([]byte(g1r[4:]))
if err != nil {
return 0, isNull, err
}
g2, err := wkb.Unmarshal([]byte(g2r[4:]))
if err != nil {
return 0, isNull, err
}
g1srid := int(binary.LittleEndian.Uint32([]byte(g1r[:4])))
g2srid := int(binary.LittleEndian.Uint32([]byte(g2r[:4])))
if g1srid != g2srid {
return 0, isNull, errors.New("SRID mismatch")
}
if g1srid != 0 {
return 0, isNull, errors.New("Distance only supported for SRID 0")
}
g1p := geom.NewPoint(g1.Layout()).MustSetCoords(g1.FlatCoords()).SetSRID(g1srid)
g2p := geom.NewPoint(g2.Layout()).MustSetCoords(g2.FlatCoords()).SetSRID(g2srid)
return xy.Distance(g1p.Coords(), g2p.Coords()), false, nil
}
Loading