Skip to content

Commit ed25698

Browse files
ohmayrparthea
andauthored
tests(sqlalchemy-bigquery): resolve ST function type binding bug (#17769)
Fixes an issue in `_fixup_st_arguments` where argument types were redundantly re-instantiated on every compiler pass due to comparing an instance with a class using is instead of `sinstance`. Also adds unit test coverage for `_fixup_st_arguments` and unskips `test_geoalchemy2_core` unit test. Fixes #17287 🦕 --------- Co-authored-by: Anthonios Partheniou <partheniou@google.com>
1 parent fbf2f43 commit ed25698

2 files changed

Lines changed: 35 additions & 10 deletions

File tree

packages/sqlalchemy-bigquery/sqlalchemy_bigquery/geography.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,8 @@ def _fixup_st_arguments(element, compiler, **kw):
183183
argument_types = _argument_types.get(element.name.lower())
184184
if argument_types:
185185
for argument_type, argument in zip(argument_types, element.clauses.clauses):
186-
if isinstance(argument, BindParameter) and (
187-
argument.type is not argument_type
188-
or not isinstance(argument.type, argument_type)
186+
if isinstance(argument, BindParameter) and not isinstance(
187+
argument.type, argument_type
189188
):
190189
argument.type = argument_type()
191190

packages/sqlalchemy-bigquery/tests/unit/test_geography.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
geoalchemy2 = pytest.importorskip("geoalchemy2")
2525

2626

27-
# TODO(http://github.com/googleapis/google-cloud-python/issues/17287): Unskip once bug is resolved.
28-
@pytest.mark.skip(reason="Failing in CI with AssertionError.")
2927
def test_geoalchemy2_core(faux_conn, last_query):
3028
"""Make sure GeoAlchemy 2 Core Tutorial works as adapted to only having geometry"""
3129
conn = faux_conn
@@ -34,7 +32,7 @@ def test_geoalchemy2_core(faux_conn, last_query):
3432

3533
from sqlalchemy import Column, String
3634

37-
from sqlalchemy_bigquery import GEOGRAPHY
35+
from sqlalchemy_bigquery import GEOGRAPHY, WKT
3836

3937
lake_table = setup_table(
4038
conn, "lake", Column("name", String), Column("geog", GEOGRAPHY)
@@ -83,7 +81,7 @@ def test_geoalchemy2_core(faux_conn, last_query):
8381
except Exception:
8482
pass # sqlite had no special functions :)
8583
last_query(
86-
"SELECT `lake`.`name`, ST_AsBinary(`lake`.`geog`) AS `geog` \n" "FROM `lake`"
84+
"SELECT `lake`.`name`, ST_AsBinary(`lake`.`geog`) AS `geog` \nFROM `lake`"
8785
)
8886

8987
# Spatial query
@@ -93,16 +91,16 @@ def test_geoalchemy2_core(faux_conn, last_query):
9391
try:
9492
conn.execute(
9593
select(lake_table.c.name).where(
96-
func.ST_Contains(lake_table.c.geog, "POINT(4 1)")
94+
func.ST_Contains(lake_table.c.geog, WKT("POINT(4 1)"))
9795
)
9896
)
9997
except Exception:
10098
pass # sqlite had no special functions :)
10199
last_query(
102100
"SELECT `lake`.`name` \n"
103101
"FROM `lake` \n"
104-
"WHERE ST_Contains(`lake`.`geog`, %(ST_Contains_1:geography)s)",
105-
{"ST_Contains_1": "POINT(4 1)"},
102+
"WHERE ST_Contains(`lake`.`geog`, ST_GeogFromText(%(ST_GeogFromText_1:STRING)s))",
103+
{"ST_GeogFromText_1": "POINT(4 1)"},
106104
)
107105

108106
try:
@@ -183,3 +181,31 @@ def test_calling_st_functions_that_dont_take_geographies(faux_conn, last_query):
183181
" AS `ST_GeogFromText_1`",
184182
dict(ST_GeogFromText_2="point(0 0)"),
185183
)
184+
185+
186+
def test_fixup_st_arguments():
187+
from geoalchemy2.functions import GenericFunction, ST_Area
188+
from sqlalchemy.sql.elements import BindParameter
189+
190+
from sqlalchemy_bigquery.geography import GEOGRAPHY, _fixup_st_arguments
191+
192+
class DummyCompiler:
193+
def visit_function(self, element, **kw):
194+
return "func(param)"
195+
196+
# Case 1: argument.type is not yet GEOGRAPHY
197+
func_element = ST_Area(BindParameter("param", "point(0 0)"))
198+
res = _fixup_st_arguments(func_element, DummyCompiler())
199+
assert res == "func(param)"
200+
assert isinstance(func_element.clauses.clauses[0].type, GEOGRAPHY)
201+
202+
# Case 2: argument.type is ALREADY GEOGRAPHY
203+
func_element2 = ST_Area(BindParameter("param", "point(0 0)", type_=GEOGRAPHY()))
204+
_fixup_st_arguments(func_element2, DummyCompiler())
205+
206+
# Case 3: function without specified argument types
207+
class ST_Unknown(GenericFunction):
208+
name = "ST_Unknown"
209+
210+
func_element3 = ST_Unknown(BindParameter("param", "point(0 0)"))
211+
_fixup_st_arguments(func_element3, DummyCompiler())

0 commit comments

Comments
 (0)