From 2e4345c69ca38a2c63e5c8dc08ced8856a0e4d6b Mon Sep 17 00:00:00 2001 From: ohmayr Date: Mon, 20 Jul 2026 18:33:38 +0000 Subject: [PATCH 1/4] fix(sqlalchemy-bigquery): resolve ST function type binding bug and unskip unit tests --- .../sqlalchemy_bigquery/geography.py | 5 ++- .../tests/unit/test_geography.py | 34 ++++++++++++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/packages/sqlalchemy-bigquery/sqlalchemy_bigquery/geography.py b/packages/sqlalchemy-bigquery/sqlalchemy_bigquery/geography.py index 670fc576b2e2..744bfc803c70 100644 --- a/packages/sqlalchemy-bigquery/sqlalchemy_bigquery/geography.py +++ b/packages/sqlalchemy-bigquery/sqlalchemy_bigquery/geography.py @@ -183,9 +183,8 @@ def _fixup_st_arguments(element, compiler, **kw): argument_types = _argument_types.get(element.name.lower()) if argument_types: for argument_type, argument in zip(argument_types, element.clauses.clauses): - if isinstance(argument, BindParameter) and ( - argument.type is not argument_type - or not isinstance(argument.type, argument_type) + if isinstance(argument, BindParameter) and not isinstance( + argument.type, argument_type ): argument.type = argument_type() diff --git a/packages/sqlalchemy-bigquery/tests/unit/test_geography.py b/packages/sqlalchemy-bigquery/tests/unit/test_geography.py index d03dbf940d93..76c4e3df8f99 100644 --- a/packages/sqlalchemy-bigquery/tests/unit/test_geography.py +++ b/packages/sqlalchemy-bigquery/tests/unit/test_geography.py @@ -24,8 +24,6 @@ geoalchemy2 = pytest.importorskip("geoalchemy2") -# TODO(http://github.com/googleapis/google-cloud-python/issues/17287): Unskip once bug is resolved. -@pytest.mark.skip(reason="Failing in CI with AssertionError.") def test_geoalchemy2_core(faux_conn, last_query): """Make sure GeoAlchemy 2 Core Tutorial works as adapted to only having geometry""" conn = faux_conn @@ -83,7 +81,7 @@ def test_geoalchemy2_core(faux_conn, last_query): except Exception: pass # sqlite had no special functions :) last_query( - "SELECT `lake`.`name`, ST_AsBinary(`lake`.`geog`) AS `geog` \n" "FROM `lake`" + "SELECT `lake`.`name`, ST_AsBinary(`lake`.`geog`) AS `geog` \nFROM `lake`" ) # Spatial query @@ -101,7 +99,7 @@ def test_geoalchemy2_core(faux_conn, last_query): last_query( "SELECT `lake`.`name` \n" "FROM `lake` \n" - "WHERE ST_Contains(`lake`.`geog`, %(ST_Contains_1:geography)s)", + "WHERE ST_Contains(`lake`.`geog`, %(ST_Contains_1:STRING)s)", {"ST_Contains_1": "POINT(4 1)"}, ) @@ -183,3 +181,31 @@ def test_calling_st_functions_that_dont_take_geographies(faux_conn, last_query): " AS `ST_GeogFromText_1`", dict(ST_GeogFromText_2="point(0 0)"), ) + + +def test_fixup_st_arguments(): + from geoalchemy2.functions import GenericFunction, ST_Area + from sqlalchemy.sql.elements import BindParameter + + from sqlalchemy_bigquery.geography import GEOGRAPHY, _fixup_st_arguments + + class DummyCompiler: + def visit_function(self, element, **kw): + return "func(param)" + + # Case 1: argument.type is not yet GEOGRAPHY + func_element = ST_Area(BindParameter("param", "point(0 0)")) + res = _fixup_st_arguments(func_element, DummyCompiler()) + assert res == "func(param)" + assert isinstance(func_element.clauses.clauses[0].type, GEOGRAPHY) + + # Case 2: argument.type is ALREADY GEOGRAPHY + func_element2 = ST_Area(BindParameter("param", "point(0 0)", type_=GEOGRAPHY())) + _fixup_st_arguments(func_element2, DummyCompiler()) + + # Case 3: function without specified argument types + class ST_Unknown(GenericFunction): + name = "ST_Unknown" + + func_element3 = ST_Unknown(BindParameter("param", "point(0 0)")) + _fixup_st_arguments(func_element3, DummyCompiler()) From 343fc309a4178d574308c31554ead74d3eccb2fe Mon Sep 17 00:00:00 2001 From: ohmayr Date: Mon, 20 Jul 2026 14:30:24 -0700 Subject: [PATCH 2/4] Update packages/sqlalchemy-bigquery/tests/unit/test_geography.py Co-authored-by: Anthonios Partheniou --- packages/sqlalchemy-bigquery/tests/unit/test_geography.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/sqlalchemy-bigquery/tests/unit/test_geography.py b/packages/sqlalchemy-bigquery/tests/unit/test_geography.py index 76c4e3df8f99..212e35cad96d 100644 --- a/packages/sqlalchemy-bigquery/tests/unit/test_geography.py +++ b/packages/sqlalchemy-bigquery/tests/unit/test_geography.py @@ -99,7 +99,8 @@ def test_geoalchemy2_core(faux_conn, last_query): last_query( "SELECT `lake`.`name` \n" "FROM `lake` \n" - "WHERE ST_Contains(`lake`.`geog`, %(ST_Contains_1:STRING)s)", + "WHERE ST_Contains(`lake`.`geog`, ST_GeogFromText(%(ST_GeogFromText_1:STRING)s))", + {"ST_GeogFromText_1": "POINT(4 1)"}, {"ST_Contains_1": "POINT(4 1)"}, ) From 226c787fb2e37840d2bd37bb9fbabc3ab1dd0281 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Mon, 20 Jul 2026 23:43:45 +0000 Subject: [PATCH 3/4] test(sqlalchemy-bigquery): revert incorrect suggestion and restore expected bind parameter compilation --- packages/sqlalchemy-bigquery/tests/unit/test_geography.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/sqlalchemy-bigquery/tests/unit/test_geography.py b/packages/sqlalchemy-bigquery/tests/unit/test_geography.py index 212e35cad96d..76c4e3df8f99 100644 --- a/packages/sqlalchemy-bigquery/tests/unit/test_geography.py +++ b/packages/sqlalchemy-bigquery/tests/unit/test_geography.py @@ -99,8 +99,7 @@ def test_geoalchemy2_core(faux_conn, last_query): last_query( "SELECT `lake`.`name` \n" "FROM `lake` \n" - "WHERE ST_Contains(`lake`.`geog`, ST_GeogFromText(%(ST_GeogFromText_1:STRING)s))", - {"ST_GeogFromText_1": "POINT(4 1)"}, + "WHERE ST_Contains(`lake`.`geog`, %(ST_Contains_1:STRING)s)", {"ST_Contains_1": "POINT(4 1)"}, ) From 5b42034dad3b16d24e67a71aad9538ce00c4ccf5 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Mon, 20 Jul 2026 23:56:32 +0000 Subject: [PATCH 4/4] test(sqlalchemy-bigquery): wrap coordinate in WKT(...) in test_geoalchemy2_core unit test --- packages/sqlalchemy-bigquery/tests/unit/test_geography.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/sqlalchemy-bigquery/tests/unit/test_geography.py b/packages/sqlalchemy-bigquery/tests/unit/test_geography.py index 76c4e3df8f99..332cb1c3a954 100644 --- a/packages/sqlalchemy-bigquery/tests/unit/test_geography.py +++ b/packages/sqlalchemy-bigquery/tests/unit/test_geography.py @@ -32,7 +32,7 @@ def test_geoalchemy2_core(faux_conn, last_query): from sqlalchemy import Column, String - from sqlalchemy_bigquery import GEOGRAPHY + from sqlalchemy_bigquery import GEOGRAPHY, WKT lake_table = setup_table( conn, "lake", Column("name", String), Column("geog", GEOGRAPHY) @@ -91,7 +91,7 @@ def test_geoalchemy2_core(faux_conn, last_query): try: conn.execute( select(lake_table.c.name).where( - func.ST_Contains(lake_table.c.geog, "POINT(4 1)") + func.ST_Contains(lake_table.c.geog, WKT("POINT(4 1)")) ) ) except Exception: @@ -99,8 +99,8 @@ def test_geoalchemy2_core(faux_conn, last_query): last_query( "SELECT `lake`.`name` \n" "FROM `lake` \n" - "WHERE ST_Contains(`lake`.`geog`, %(ST_Contains_1:STRING)s)", - {"ST_Contains_1": "POINT(4 1)"}, + "WHERE ST_Contains(`lake`.`geog`, ST_GeogFromText(%(ST_GeogFromText_1:STRING)s))", + {"ST_GeogFromText_1": "POINT(4 1)"}, ) try: