From b7113fcfd00adfc51983e10a381172268e856918 Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Tue, 11 Aug 2026 13:23:44 +0900 Subject: [PATCH 1/2] Enable SQLAlchemy update and delete tests --- tests/sqlalchemy/test_suite.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/sqlalchemy/test_suite.py b/tests/sqlalchemy/test_suite.py index 4ffdd9cc..6aa74088 100644 --- a/tests/sqlalchemy/test_suite.py +++ b/tests/sqlalchemy/test_suite.py @@ -1,9 +1,11 @@ import pytest +from sqlalchemy.testing import eq_ from sqlalchemy.testing.suite import * # noqa: F403 from sqlalchemy.testing.suite import FetchLimitOffsetTest as _FetchLimitOffsetTest from sqlalchemy.testing.suite import HasTableTest as _HasTableTest from sqlalchemy.testing.suite import InsertBehaviorTest as _InsertBehaviorTest from sqlalchemy.testing.suite import IntegerTest as _IntegerTest +from sqlalchemy.testing.suite import SimpleUpdateDeleteTest as _SimpleUpdateDeleteTest from sqlalchemy.testing.suite import StringTest as _StringTest from sqlalchemy.testing.suite import TrueDivTest as _TrueDivTest @@ -21,13 +23,42 @@ del LongNameBlowoutTest # noqa: F821 del QuotedNameArgumentTest # noqa: F821 del RowCountTest # noqa: F821 -del SimpleUpdateDeleteTest # noqa: F821 del TimeMicrosecondsTest # noqa: F821 del TimeTest # noqa: F821 del TimestampMicrosecondsTest # noqa: F821 del UuidTest # noqa: F821 +class SimpleUpdateDeleteTest(_SimpleUpdateDeleteTest): + # Athena supports UPDATE and DELETE for Iceberg tables but does not report reliable row counts. + __requires__ = () + + def test_update(self, connection): + table = self.tables.plain_pk + result = connection.execute( + table.update().where(table.c.id == 2), + {"data": "d2_new"}, + ) + + assert not result.is_insert + assert not result.returns_rows + eq_( + connection.execute(table.select().order_by(table.c.id)).fetchall(), + [(1, "d1"), (2, "d2_new"), (3, "d3")], + ) + + def test_delete(self, connection): + table = self.tables.plain_pk + result = connection.execute(table.delete().where(table.c.id == 2)) + + assert not result.is_insert + assert not result.returns_rows + eq_( + connection.execute(table.select().order_by(table.c.id)).fetchall(), + [(1, "d1"), (3, "d3")], + ) + + class HasTableTest(_HasTableTest): @pytest.mark.skip("No cache is used when creating tables.") def test_has_table_cache(self, metadata): From d71b3d7c017363999e21a474832431eb2ae43d5d Mon Sep 17 00:00:00 2001 From: laughingman7743 Date: Tue, 11 Aug 2026 13:38:07 +0900 Subject: [PATCH 2/2] Report no result rows for Athena DML --- pyathena/result_set.py | 6 +++++- tests/pyathena/test_cursor.py | 4 ++++ tests/sqlalchemy/test_suite.py | 32 -------------------------------- 3 files changed, 9 insertions(+), 33 deletions(-) diff --git a/pyathena/result_set.py b/pyathena/result_set.py index c24b749f..5010dd97 100644 --- a/pyathena/result_set.py +++ b/pyathena/result_set.py @@ -56,6 +56,7 @@ class AthenaResultSet(CursorIterator): # https://docs.aws.amazon.com/athena/latest/ug/data-types.html # Athena complex types that benefit from type hint conversion. _COMPLEX_TYPES: frozenset[str] = frozenset({"array", "map", "row", "struct"}) + _DML_SUBSTATEMENT_TYPES: frozenset[str] = frozenset({"INSERT", "UPDATE", "DELETE", "MERGE"}) def __init__( self, @@ -318,7 +319,10 @@ def result_reuse_minutes(self) -> int | None: def description( self, ) -> list[tuple[str, str, None, None, int, int, str]] | None: - if self._metadata is None: + if self._metadata is None or ( + self.substatement_type + and self.substatement_type.upper() in self._DML_SUBSTATEMENT_TYPES + ): return None return [ ( diff --git a/tests/pyathena/test_cursor.py b/tests/pyathena/test_cursor.py index f7d84638..2e3c2be3 100644 --- a/tests/pyathena/test_cursor.py +++ b/tests/pyathena/test_cursor.py @@ -826,6 +826,7 @@ def test_iceberg_table(self, cursor): VALUES (1, 'test1'), (2, 'test2') """ ) + assert cursor.description is None assert cursor.rowcount == 2 cursor.execute( f""" @@ -842,6 +843,7 @@ def test_iceberg_table(self, cursor): WHERE id = 1 """ ) + assert cursor.description is None assert cursor.rowcount == 1 cursor.execute( f""" @@ -873,6 +875,7 @@ def test_iceberg_table(self, cursor): THEN UPDATE SET col1 = t2.col1 """ ) + assert cursor.description is None assert cursor.rowcount == 1 cursor.execute( f""" @@ -895,6 +898,7 @@ def test_iceberg_table(self, cursor): WHERE id = 2 """ ) + assert cursor.description is None assert cursor.rowcount == 1 cursor.execute( f""" diff --git a/tests/sqlalchemy/test_suite.py b/tests/sqlalchemy/test_suite.py index 6aa74088..49077eef 100644 --- a/tests/sqlalchemy/test_suite.py +++ b/tests/sqlalchemy/test_suite.py @@ -1,11 +1,9 @@ import pytest -from sqlalchemy.testing import eq_ from sqlalchemy.testing.suite import * # noqa: F403 from sqlalchemy.testing.suite import FetchLimitOffsetTest as _FetchLimitOffsetTest from sqlalchemy.testing.suite import HasTableTest as _HasTableTest from sqlalchemy.testing.suite import InsertBehaviorTest as _InsertBehaviorTest from sqlalchemy.testing.suite import IntegerTest as _IntegerTest -from sqlalchemy.testing.suite import SimpleUpdateDeleteTest as _SimpleUpdateDeleteTest from sqlalchemy.testing.suite import StringTest as _StringTest from sqlalchemy.testing.suite import TrueDivTest as _TrueDivTest @@ -29,36 +27,6 @@ del UuidTest # noqa: F821 -class SimpleUpdateDeleteTest(_SimpleUpdateDeleteTest): - # Athena supports UPDATE and DELETE for Iceberg tables but does not report reliable row counts. - __requires__ = () - - def test_update(self, connection): - table = self.tables.plain_pk - result = connection.execute( - table.update().where(table.c.id == 2), - {"data": "d2_new"}, - ) - - assert not result.is_insert - assert not result.returns_rows - eq_( - connection.execute(table.select().order_by(table.c.id)).fetchall(), - [(1, "d1"), (2, "d2_new"), (3, "d3")], - ) - - def test_delete(self, connection): - table = self.tables.plain_pk - result = connection.execute(table.delete().where(table.c.id == 2)) - - assert not result.is_insert - assert not result.returns_rows - eq_( - connection.execute(table.select().order_by(table.c.id)).fetchall(), - [(1, "d1"), (3, "d3")], - ) - - class HasTableTest(_HasTableTest): @pytest.mark.skip("No cache is used when creating tables.") def test_has_table_cache(self, metadata):