Skip to content

Commit 91a7b9d

Browse files
RainbowDashyclaude
andauthored
fix(tsql): add GEOGRAPHY/GEOMETRY static method support (#56)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent e9723d1 commit 91a7b9d

File tree

8 files changed

+19249
-18938
lines changed

8 files changed

+19249
-18938
lines changed

tsql/TSqlParser.g4

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4201,6 +4201,7 @@ function_call
42014201
| freetext_function #FREE_TEXT
42024202
| partition_function #PARTITION_FUNC
42034203
| hierarchyid_static_method #HIERARCHYID_METHOD
4204+
| spatial_static_method #SPATIAL_METHOD
42044205
;
42054206

42064207
partition_function
@@ -4733,6 +4734,10 @@ hierarchyid_static_method
47334734
: HIERARCHYID DOUBLE_COLON (GETROOT '(' ')' | PARSE '(' input=expression ')')
47344735
;
47354736

4737+
spatial_static_method
4738+
: (GEOGRAPHY | GEOMETRY) DOUBLE_COLON id_ '(' expression_list_? ')'
4739+
;
4740+
47364741
nodes_method
47374742
: (loc_id=LOCAL_ID | value_id=full_column_name | '(' subquery ')') '.' NODES '(' xquery=STRING ')'
47384743
;
@@ -5541,6 +5546,7 @@ keyword
55415546
| PAGECOUNT
55425547
| PAGLOCK
55435548
| PARAMETERIZATION
5549+
| PARSE
55445550
| PARSENAME
55455551
| PARSEONLY
55465552
| PARTITION
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
-- Spatial Static Method Test Cases
2+
-- Testing GEOGRAPHY::method() and GEOMETRY::method() static method calls
3+
4+
-- Basic geography static methods
5+
SELECT geography::STGeomFromText('POINT(1 2)', 4326);
6+
SELECT geography::STPointFromText('POINT(-122.34900 47.65100)', 4326);
7+
SELECT geography::STLineFromText('LINESTRING(-122.360 47.656, -122.343 47.656)', 4326);
8+
SELECT geography::STPolyFromText('POLYGON((-122.358 47.653, -122.348 47.649, -122.348 47.658, -122.358 47.658, -122.358 47.653))', 4326);
9+
SELECT geography::Point(47.65100, -122.34900, 4326);
10+
SELECT geography::Parse('POINT(-122.34900 47.65100)');
11+
12+
-- Basic geometry static methods
13+
SELECT geometry::STGeomFromText('POINT(1 2)', 0);
14+
SELECT geometry::STGeomFromText('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))', 0);
15+
SELECT geometry::STPointFromText('POINT(3 4)', 0);
16+
SELECT geometry::STLineFromText('LINESTRING(0 0, 1 1, 2 1, 2 2)', 0);
17+
SELECT geometry::STPolyFromText('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', 0);
18+
SELECT geometry::Parse('LINESTRING(0 0, 1 1)');
19+
SELECT geometry::STGeomFromWKB(0x0101000000000000000000F03F0000000000000040, 0);
20+
21+
-- Multi-geometry methods
22+
SELECT geometry::STMPointFromText('MULTIPOINT((1 1), (2 2), (3 3))', 0);
23+
SELECT geometry::STMLineFromText('MULTILINESTRING((0 0, 1 1), (2 2, 3 3))', 0);
24+
SELECT geometry::STMPolyFromText('MULTIPOLYGON(((0 0, 1 0, 1 1, 0 1, 0 0)), ((2 2, 3 2, 3 3, 2 3, 2 2)))', 0);
25+
SELECT geometry::STGeomCollFromText('GEOMETRYCOLLECTION(POINT(1 1), LINESTRING(0 0, 1 1))', 0);
26+
27+
-- Spatial methods in variable assignments
28+
DECLARE @g geography = geography::STGeomFromText('POINT(1 2)', 4326);
29+
DECLARE @point geometry = geometry::Point(3, 4, 0);
30+
31+
-- Spatial methods in INSERT statements
32+
INSERT INTO GeoTable (Location) VALUES (geography::STGeomFromText('POINT(-122.34900 47.65100)', 4326));
33+
INSERT INTO GeoTable (Shape) VALUES (geometry::STGeomFromText('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', 0));
34+
35+
-- Spatial methods in UPDATE statements
36+
UPDATE GeoTable SET Location = geography::STGeomFromText('POINT(-122.35 47.65)', 4326) WHERE ID = 1;
37+
UPDATE GeoTable SET Shape = geometry::Parse('POINT(5 5)') WHERE ID = 2;
38+
39+
-- Multiple spatial calls in single statement
40+
SELECT
41+
geography::Point(47.65, -122.35, 4326) AS Point1,
42+
geography::Point(47.66, -122.36, 4326) AS Point2,
43+
geometry::STGeomFromText('POINT(0 0)', 0) AS Origin;
44+
45+
-- Spatial methods in expressions
46+
SELECT 1 WHERE geography::STGeomFromText('POINT(1 2)', 4326) IS NOT NULL;
47+
48+
-- Spatial methods in CASE expressions
49+
SELECT CASE WHEN 1=1 THEN geography::Point(1, 2, 4326) ELSE geography::Point(0, 0, 4326) END;
50+
51+
-- Spatial methods with variables
52+
DECLARE @lat float = 47.65;
53+
DECLARE @lon float = -122.35;
54+
DECLARE @srid int = 4326;
55+
SELECT geography::Point(@lat, @lon, @srid);
56+
57+
-- Spatial methods in subqueries
58+
SELECT * FROM (SELECT geography::Point(1, 2, 4326) AS Location) AS sub;
59+
60+
-- Spatial methods with column references in function arguments
61+
SELECT geography::STGeomFromText(WKTColumn, SRIDColumn) FROM GeoTable;

tsql/parser_test.go

Lines changed: 3 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tsql_test
33
import (
44
"os"
55
"path"
6+
"strings"
67
"testing"
78

89
"github.com/antlr4-go/antlr/v4"
@@ -40,11 +41,11 @@ func TestTSQLParser(t *testing.T) {
4041
require.NoError(t, err)
4142

4243
for _, file := range examples {
43-
if file.Name() != "test.sql" {
44+
if !strings.HasSuffix(file.Name(), ".sql") {
4445
continue
4546
}
4647
filePath := path.Join("examples", file.Name())
47-
t.Run(filePath, func(t *testing.T) {
48+
t.Run(file.Name(), func(t *testing.T) {
4849
t.Parallel()
4950
input, err := antlr.NewFileStream(filePath)
5051
require.NoError(t, err)
@@ -71,81 +72,3 @@ func TestTSQLParser(t *testing.T) {
7172
})
7273
}
7374
}
74-
75-
func TestSpatialIndexParser(t *testing.T) {
76-
filePath := "examples/spatial_index.sql"
77-
input, err := antlr.NewFileStream(filePath)
78-
require.NoError(t, err)
79-
80-
lexer := tsql.NewTSqlLexer(input)
81-
82-
stream := antlr.NewCommonTokenStream(lexer, 0)
83-
p := tsql.NewTSqlParser(stream)
84-
85-
lexerErrors := &CustomErrorListener{}
86-
lexer.RemoveErrorListeners()
87-
lexer.AddErrorListener(lexerErrors)
88-
89-
parserErrors := &CustomErrorListener{}
90-
p.RemoveErrorListeners()
91-
p.AddErrorListener(parserErrors)
92-
93-
p.BuildParseTrees = true
94-
95-
_ = p.Tsql_file()
96-
97-
require.Equal(t, 0, lexerErrors.errors)
98-
require.Equal(t, 0, parserErrors.errors)
99-
}
100-
101-
func TestDDLIndexParser(t *testing.T) {
102-
filePath := "examples/ddl_index.sql"
103-
input, err := antlr.NewFileStream(filePath)
104-
require.NoError(t, err)
105-
106-
lexer := tsql.NewTSqlLexer(input)
107-
108-
stream := antlr.NewCommonTokenStream(lexer, 0)
109-
p := tsql.NewTSqlParser(stream)
110-
111-
lexerErrors := &CustomErrorListener{}
112-
lexer.RemoveErrorListeners()
113-
lexer.AddErrorListener(lexerErrors)
114-
115-
parserErrors := &CustomErrorListener{}
116-
p.RemoveErrorListeners()
117-
p.AddErrorListener(parserErrors)
118-
119-
p.BuildParseTrees = true
120-
121-
_ = p.Tsql_file()
122-
123-
require.Equal(t, 0, lexerErrors.errors)
124-
require.Equal(t, 0, parserErrors.errors)
125-
}
126-
127-
func TestSpatialComprehensiveParser(t *testing.T) {
128-
filePath := "examples/spatial_comprehensive.sql"
129-
input, err := antlr.NewFileStream(filePath)
130-
require.NoError(t, err)
131-
132-
lexer := tsql.NewTSqlLexer(input)
133-
134-
stream := antlr.NewCommonTokenStream(lexer, 0)
135-
p := tsql.NewTSqlParser(stream)
136-
137-
lexerErrors := &CustomErrorListener{}
138-
lexer.RemoveErrorListeners()
139-
lexer.AddErrorListener(lexerErrors)
140-
141-
parserErrors := &CustomErrorListener{}
142-
p.RemoveErrorListeners()
143-
p.AddErrorListener(parserErrors)
144-
145-
p.BuildParseTrees = true
146-
147-
_ = p.Tsql_file()
148-
149-
require.Equal(t, 0, lexerErrors.errors)
150-
require.Equal(t, 0, parserErrors.errors)
151-
}

0 commit comments

Comments
 (0)