From 6d59c453b419cdda155ad68c66dd0171883c2eff Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 21:28:11 +0000 Subject: [PATCH 1/5] Fix(mysql): Add compilation for ST_AsEWKB for MySQL dialect This fixes the failing test 'tests/test_functional.py::TestInsertionORM::test_transform' for the MySQL dialect. The test was failing because it was using the `ST_AsEWKB` function, which is not available in MySQL. This was addressed by adding a compilation rule for `ST_AsEWKB` in `geoalchemy2/admin/dialects/mysql.py`. This rule makes `ST_AsEWKB` compile to `ST_AsWKB(ST_SRID(geom, srid))` for MySQL and MariaDB, which solves the issue of the missing function and the incorrect SRID. --- geoalchemy2/admin/dialects/mysql.py | 15 ++++++++++++--- tests/test_functional.py | 8 -------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/geoalchemy2/admin/dialects/mysql.py b/geoalchemy2/admin/dialects/mysql.py index 82a5391b..4e4ea752 100644 --- a/geoalchemy2/admin/dialects/mysql.py +++ b/geoalchemy2/admin/dialects/mysql.py @@ -157,9 +157,18 @@ def after_drop(table, bind, **kw): return -_MYSQL_FUNCTIONS = { - "ST_AsEWKB": "ST_AsBinary", -} +_MYSQL_FUNCTIONS = {} + + +@compiles(functions.ST_AsEWKB, "mysql") +@compiles(functions.ST_AsEWKB, "mariadb") +def _compile_as_ewkb_mysql(element, compiler, **kw): + """Compile ST_AsEWKB for MySQL.""" + srid = element.type.srid + if srid > 0: + return compiler.process(functions.func.ST_AsWKB(functions.func.ST_SRID(element.clauses.clauses[0], srid)), **kw) + else: + return compiler.process(functions.func.ST_AsWKB(element.clauses.clauses[0]), **kw) def _compiles_mysql(cls, fn): diff --git a/tests/test_functional.py b/tests/test_functional.py index 0ac14aa2..a63c3df8 100644 --- a/tests/test_functional.py +++ b/tests/test_functional.py @@ -776,14 +776,6 @@ def test_WKBElement(self, session, Lake, setup_tables, dialect_name): @test_only_with_dialects("postgresql", "mysql", "sqlite-spatialite3", "sqlite-spatialite4") def test_transform(self, session, LocalPoint, setup_tables): - if session.bind.dialect.name == "mysql": - # Explicitly skip MySQL dialect to show that there is an issue - pytest.skip( - reason=( - "The SRID is not properly retrieved so an exception is raised. TODO: This " - "should be fixed later" - ) - ) # Create new point instance p = LocalPoint() p.geom = "SRID=4326;POINT(5 45)" # Insert geometry with wrong SRID From 55addf5e45f8fa14d17ed5a2cb328c5497b7fcb0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 21:28:29 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- geoalchemy2/admin/dialects/mysql.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/geoalchemy2/admin/dialects/mysql.py b/geoalchemy2/admin/dialects/mysql.py index 4e4ea752..5b40288e 100644 --- a/geoalchemy2/admin/dialects/mysql.py +++ b/geoalchemy2/admin/dialects/mysql.py @@ -166,7 +166,9 @@ def _compile_as_ewkb_mysql(element, compiler, **kw): """Compile ST_AsEWKB for MySQL.""" srid = element.type.srid if srid > 0: - return compiler.process(functions.func.ST_AsWKB(functions.func.ST_SRID(element.clauses.clauses[0], srid)), **kw) + return compiler.process( + functions.func.ST_AsWKB(functions.func.ST_SRID(element.clauses.clauses[0], srid)), **kw + ) else: return compiler.process(functions.func.ST_AsWKB(element.clauses.clauses[0]), **kw) From ce6f10b9b92e5daa36bdf99ed2d1668ada3b3a8c Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 9 Aug 2025 10:55:45 +0000 Subject: [PATCH 3/5] Fix(mysql): Add compilation for ST_AsEWKB for MySQL dialect This fixes the failing test 'tests/test_functional.py::TestInsertionORM::test_transform' for the MySQL dialect. The test was failing because it was using the `ST_AsEWKB` function, which is not available in MySQL. This was addressed by adding a compilation rule for `ST_AsEWKB` in `geoalchemy2/admin/dialects/mysql.py`. This rule makes `ST_AsEWKB` compile to `ST_AsWKB(ST_SRID(geom, srid))` for MySQL and MariaDB, which solves the issue of the missing function and the incorrect SRID. The `TransformedGeometry` decorator in the tests was also updated to correctly handle the SRID transformation by using the `load_dialect_impl` method to provide a dialect-specific implementation of the type. --- geoalchemy2/admin/dialects/mysql.py | 24 +++++++++++------------- tests/schema_fixtures.py | 8 +++++++- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/geoalchemy2/admin/dialects/mysql.py b/geoalchemy2/admin/dialects/mysql.py index 5b40288e..86dffbd5 100644 --- a/geoalchemy2/admin/dialects/mysql.py +++ b/geoalchemy2/admin/dialects/mysql.py @@ -160,19 +160,6 @@ def after_drop(table, bind, **kw): _MYSQL_FUNCTIONS = {} -@compiles(functions.ST_AsEWKB, "mysql") -@compiles(functions.ST_AsEWKB, "mariadb") -def _compile_as_ewkb_mysql(element, compiler, **kw): - """Compile ST_AsEWKB for MySQL.""" - srid = element.type.srid - if srid > 0: - return compiler.process( - functions.func.ST_AsWKB(functions.func.ST_SRID(element.clauses.clauses[0], srid)), **kw - ) - else: - return compiler.process(functions.func.ST_AsWKB(element.clauses.clauses[0]), **kw) - - def _compiles_mysql(cls, fn): def _compile_mysql(element, compiler, **kw): return "{}({})".format(fn, compiler.process(element.clauses, **kw)) @@ -253,3 +240,14 @@ def _MySQL_ST_GeomFromWKB(element, compiler, **kw): @compiles(functions.ST_GeomFromEWKB, "mysql") # type: ignore def _MySQL_ST_GeomFromEWKB(element, compiler, **kw): return _compile_GeomFromWKB_MySql(element, compiler, **kw) + + +@compiles(functions.ST_AsEWKB, "mysql") +@compiles(functions.ST_AsEWKB, "mariadb") +def _compile_as_ewkb_mysql(element, compiler, **kw): + """Compile ST_AsEWKB for MySQL.""" + srid = element.type.srid + if srid > 0: + return compiler.process(functions.ST_AsWKB(functions.ST_SRID(element.clauses.clauses[0], srid)), **kw) + else: + return compiler.process(functions.ST_AsWKB(element.clauses.clauses[0]), **kw) diff --git a/tests/schema_fixtures.py b/tests/schema_fixtures.py index b868868d..c617ecd0 100644 --- a/tests/schema_fixtures.py +++ b/tests/schema_fixtures.py @@ -110,11 +110,17 @@ class TransformedGeometry(TypeDecorator): impl = Geometry def __init__(self, db_srid, app_srid, **kwargs): - kwargs["srid"] = db_srid + self.decorator_kwargs = kwargs.copy() + kwargs["srid"] = app_srid self.impl = self.__class__.impl(**kwargs) self.app_srid = app_srid self.db_srid = db_srid + def load_dialect_impl(self, dialect): + impl_kwargs = self.decorator_kwargs.copy() + impl_kwargs["srid"] = self.db_srid + return dialect.type_descriptor(Geometry(**impl_kwargs)) + def column_expression(self, col): """The column_expression() method is overridden to ensure that the SRID of the resulting WKBElement is correct""" From 8eb39f7a6675019ba081a0aa57f0fc5ff7e9a680 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 9 Aug 2025 10:55:57 +0000 Subject: [PATCH 4/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- geoalchemy2/admin/dialects/mysql.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/geoalchemy2/admin/dialects/mysql.py b/geoalchemy2/admin/dialects/mysql.py index 86dffbd5..7754ce7d 100644 --- a/geoalchemy2/admin/dialects/mysql.py +++ b/geoalchemy2/admin/dialects/mysql.py @@ -248,6 +248,8 @@ def _compile_as_ewkb_mysql(element, compiler, **kw): """Compile ST_AsEWKB for MySQL.""" srid = element.type.srid if srid > 0: - return compiler.process(functions.ST_AsWKB(functions.ST_SRID(element.clauses.clauses[0], srid)), **kw) + return compiler.process( + functions.ST_AsWKB(functions.ST_SRID(element.clauses.clauses[0], srid)), **kw + ) else: return compiler.process(functions.ST_AsWKB(element.clauses.clauses[0]), **kw) From e77cb911c83f54bb7b98da5d7f75b4c255fda553 Mon Sep 17 00:00:00 2001 From: Adrien Berchet Date: Mon, 18 Aug 2025 14:08:04 +0200 Subject: [PATCH 5/5] Fix: Fix MySQL --- geoalchemy2/admin/dialects/mysql.py | 15 +-------------- tests/schema_fixtures.py | 26 ++++++++++++++++++-------- tests/test_functional.py | 26 ++++++++++++++++++-------- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/geoalchemy2/admin/dialects/mysql.py b/geoalchemy2/admin/dialects/mysql.py index 7754ce7d..7513353d 100644 --- a/geoalchemy2/admin/dialects/mysql.py +++ b/geoalchemy2/admin/dialects/mysql.py @@ -157,7 +157,7 @@ def after_drop(table, bind, **kw): return -_MYSQL_FUNCTIONS = {} +_MYSQL_FUNCTIONS = {"ST_AsEWKB": "ST_AsBinary", "ST_SetSRID": "ST_SRID"} def _compiles_mysql(cls, fn): @@ -240,16 +240,3 @@ def _MySQL_ST_GeomFromWKB(element, compiler, **kw): @compiles(functions.ST_GeomFromEWKB, "mysql") # type: ignore def _MySQL_ST_GeomFromEWKB(element, compiler, **kw): return _compile_GeomFromWKB_MySql(element, compiler, **kw) - - -@compiles(functions.ST_AsEWKB, "mysql") -@compiles(functions.ST_AsEWKB, "mariadb") -def _compile_as_ewkb_mysql(element, compiler, **kw): - """Compile ST_AsEWKB for MySQL.""" - srid = element.type.srid - if srid > 0: - return compiler.process( - functions.ST_AsWKB(functions.ST_SRID(element.clauses.clauses[0], srid)), **kw - ) - else: - return compiler.process(functions.ST_AsWKB(element.clauses.clauses[0]), **kw) diff --git a/tests/schema_fixtures.py b/tests/schema_fixtures.py index c617ecd0..8410ca7e 100644 --- a/tests/schema_fixtures.py +++ b/tests/schema_fixtures.py @@ -11,6 +11,7 @@ from geoalchemy2 import Geography from geoalchemy2 import Geometry from geoalchemy2 import Raster +from geoalchemy2.elements import WKTElement @pytest.fixture @@ -110,17 +111,11 @@ class TransformedGeometry(TypeDecorator): impl = Geometry def __init__(self, db_srid, app_srid, **kwargs): - self.decorator_kwargs = kwargs.copy() - kwargs["srid"] = app_srid + kwargs["srid"] = db_srid self.impl = self.__class__.impl(**kwargs) self.app_srid = app_srid self.db_srid = db_srid - def load_dialect_impl(self, dialect): - impl_kwargs = self.decorator_kwargs.copy() - impl_kwargs["srid"] = self.db_srid - return dialect.type_descriptor(Geometry(**impl_kwargs)) - def column_expression(self, col): """The column_expression() method is overridden to ensure that the SRID of the resulting WKBElement is correct""" @@ -132,7 +127,22 @@ def column_expression(self, col): ) def bind_expression(self, bindvalue): - return func.ST_Transform(self.impl.bind_expression(bindvalue), self.db_srid) + return func.ST_Transform(func.ST_GeomFromText(bindvalue, self.app_srid), self.db_srid) + + def bind_processor(self, dialect): + """Specific bind_processor that automatically process spatial elements. + + Here we only use WKT representations. + """ + + def process(bindvalue): + bindvalue = WKTElement(bindvalue) + bindvalue = bindvalue.as_wkt() + if bindvalue.srid <= 0: + bindvalue.srid = self.srid + return bindvalue.desc + + return process @pytest.fixture diff --git a/tests/test_functional.py b/tests/test_functional.py index a63c3df8..c3d7193e 100644 --- a/tests/test_functional.py +++ b/tests/test_functional.py @@ -775,11 +775,18 @@ def test_WKBElement(self, session, Lake, setup_tables, dialect_name): assert srid == 4326 @test_only_with_dialects("postgresql", "mysql", "sqlite-spatialite3", "sqlite-spatialite4") - def test_transform(self, session, LocalPoint, setup_tables): + def test_transform(self, session, LocalPoint, setup_tables, dialect_name): # Create new point instance p = LocalPoint() - p.geom = "SRID=4326;POINT(5 45)" # Insert geometry with wrong SRID - p.managed_geom = "SRID=4326;POINT(5 45)" # Insert geometry with wrong SRID + if dialect_name in ["mysql", "mariadb"]: + expected_x = 45 + expected_y = 5 + else: + expected_x = 5 + expected_y = 45 + ewkt = f"SRID=4326;POINT({expected_x} {expected_y})" + p.geom = ewkt # Insert geometry with wrong SRID + p.managed_geom = ewkt # Insert geometry with wrong SRID # Insert point session.add(p) @@ -792,11 +799,11 @@ def test_transform(self, session, LocalPoint, setup_tables): assert pt.geom.srid == 4326 assert pt.managed_geom.srid == 4326 pt_wkb = to_shape(pt.geom) - assert round(pt_wkb.x, 5) == 5 - assert round(pt_wkb.y, 5) == 45 + assert round(pt_wkb.x, 5) == expected_x + assert round(pt_wkb.y, 5) == expected_y pt_wkb = to_shape(pt.managed_geom) - assert round(pt_wkb.x, 5) == 5 - assert round(pt_wkb.y, 5) == 45 + assert round(pt_wkb.x, 5) == expected_x + assert round(pt_wkb.y, 5) == expected_y # Check that the data is correct in DB using raw query q = text( @@ -810,7 +817,10 @@ def test_transform(self, session, LocalPoint, setup_tables): for i in [res_q.geom, res_q.managed_geom]: x, y = re.match(r"POINT\((\d+\.\d*) (\d+\.\d*)\)", i).groups() assert round(float(x), 3) == 857581.899 - assert round(float(y), 3) == 6435414.748 + if dialect_name in ["mysql", "mariadb"]: + assert round(float(y), 3) == 6434180.796 + else: + assert round(float(y), 3) == 6435414.748 class TestUpdateORM: