Skip to content

Remove and rewrite unnecessary tests in the SQLAlchemy dialect tests #1242

Closed

Description

@laughingman7743

Summary

Part of #1239. Files: sqlalchemy/test_base.py, sqlalchemy/test_compiler.py, sqlalchemy/test_array.py, sqlalchemy/test_struct.py, sqlalchemy/test_map.py, sqlalchemy/test_types.py, aio/sqlalchemy/test_base.py (sqlalchemy/test_temporal.py has no findings). At master 59169c5, with the cross-check corrections, the files have 333 test functions: 237 KEEP, 62 REMOVE, 15 MERGE, 19 REWRITE. The changes save about 140 Athena queries and 2 metadata API calls per run (100 from the binary merges, about 31 from the across-cursor reductions, 5 from the nested-struct removals, 2 from the test_filter_func rewrite, 1 each from the connection-level callback and aio unicode removals), plus 3 queries where S3 Tables are configured. The compliance suite under tests/sqlalchemy/ is not changed.

Paths are relative to tests/pyathena/. [original] marks a test that existed at 21cbf05 (v3.15.0).

Order

Findings

1. sqlalchemy/test_base.py

test change criteria reason covered by
TestAthenaDialect::test_do_ping_uses_api_cursor_without_unload REMOVE 1, 2 It patches Cursor.execute/close and asserts the exact operation and close sequence; the live tests check a working ping with UNLOAD on. TestSQLAlchemyAthena::test_ping_with_unload; aio/sqlalchemy/test_base.py::TestAsyncSQLAlchemyAthena::test_ping_with_unload
TestAthenaDialect::test_do_ping_keeps_default_dialect_ping REMOVE 1, 5 It replaces DefaultDialect.do_ping, Cursor.execute and is_disconnect with stand-ins and checks that the dialect calls the stand-in. TestSQLAlchemyAthena::test_ping_with_unload
TestAthenaDialect::test_type_compiler REMOVE 6, 1 It asserts class attributes (type_compiler_cls is AthenaTypeCompiler); a wrong compiler fails every DDL compile test. sqlalchemy/test_compiler.py::TestAthenaDDLCompiler (all DDL tests); TestSQLAlchemyAthena::test_insertmanyvalues
TestAthenaDialect::test_columns_from_information_schema REWRITE 1 Assert the row sorting, comments, partition flags, type mapping and O'Neil escaping; drop opened == [Cursor] and the exact kwargs == {"result_reuse_enable": False}. API-cursor use: TestSQLAlchemyAthena::test_throttled_columns_across_cursor_types
TestAthenaDialect::test_without_fallback_retries_keeps_other_codes REMOVE 7, 2 It unit-tests the private staticmethod _without_fallback_retries; its observable effect, no retry before the fallback, is checked elsewhere. TestAthenaDialect::test_column_lookup_retry_config[connection]; TestSQLAlchemyAthena::test_throttled_reflection_reads_glue
TestAthenaDialect::test_cursor_schema_applies_to_lookup_fallback_and_cache REWRITE 1 Assert that the cursor_kwargs schema reaches both lookups; drop the exact request dict (logging_: False) and the private cache-key tuple ("pyathena_information_schema_columns", ...). —
TestAthenaDialect::test_table_level_reflection_reuses_reflected_columns REWRITE 1 Fill info_cache with a public get_columns(..., info_cache=...) call before get_table_options instead of seeding the private key layout, and assert that no second lookup runs. —
TestAthenaDialect::test_get_view_definition_keeps_blank_lines REMOVE 1, 2 It asserts opened == [Cursor] and the exact SHOW CREATE VIEW text through fakes; the live test checks the blank lines against what Athena returns. TestSQLAlchemyAthena::test_get_view_definition_across_cursor_types (after its REWRITE, which keeps the check)
TestAthenaDialect::test_get_view_definition_reports_a_missing_view REMOVE 2 It hard-codes Athena's failure message in a fake; the live test exercises the real message and the same NoSuchTableError contract. TestSQLAlchemyAthena::test_get_view_definition_missing_view [original]
TestAthenaDialect::test_get_table_matches_long_names_case_insensitively REWRITE 1 Assert that a name over 128 characters resolves through the listing to the catalog's casing; drop the exact list_table_metadata kwargs and the private ("pyathena_table_metadata", ...) cache key. —
TestAthenaDialect::test_insertmanyvalues_page_size REMOVE 2, 6 Passing insertmanyvalues_page_size to create_engine or execution_options is SQLAlchemy's feature; the dialect's precedence logic is tested separately. TestAthenaDialect::test_insertmanyvalues_keyword_overrides_url; TestSQLAlchemyAthena::test_insertmanyvalues
TestAthenaDialect::test_insertmanyvalues_disabled REMOVE 2 The use_insertmanyvalues=False fallback to executemany is covered; the remaining executemany SQL/params assertion is SQLAlchemy's rendering. TestAthenaDialect::test_insertmanyvalues_url_options[use_insertmanyvalues=false]; TestAthenaDialect::test_insertmanyvalues_keyword_overrides_url (case 0)
TestSQLAlchemyAthena::test_select_nested_struct_query, TestSQLAlchemyAthena::test_select_array_with_nested_struct REMOVE 2, 4 text() queries do no SQLAlchemy type processing, so these re-test the DB API converter's nested ROW parsing (#627) with 5 queries. test_converter.py::test_to_struct_athena_nested_formats; test_converter.py::test_to_array_athena_nested_struct_elements; test_cursor.py::TestCursor::test_complex_with_type_hints
TestSQLAlchemyAthena::test_reflect_table [original] REWRITE 2 Commit 7793651 (2025-08-03) changed MetaData() to MetaData(schema=ENV.schema), which made it a copy of test_reflect_table_with_schema; restore MetaData() so it reflects through the connection's default schema. TestSQLAlchemyAthena::test_reflect_table_with_schema [original] (identical today)
TestSQLAlchemyAthena::test_throttled_columns_across_cursor_types REWRITE 4 The API cursor is the shared AthenaDialect._internal_cursor, so keep one DataFrame case (pandas with UNLOAD) and drop the other five (about 15 queries). —
TestSQLAlchemyAthena::test_get_view_definition_across_cursor_types REWRITE 4 Keep one DataFrame case (pandas with UNLOAD) and its blank-line assertion; drop the other four (16 queries). —
TestSQLAlchemyAthena::test_filter_func REWRITE 5, 4 The assertions isinstance(result, list) and len(result) >= 0 pass for any array; run one query and assert filter(col_array, x -> x > 1) returns [2] for col_array = [1, 2]. compile side: sqlalchemy/test_compiler.py::TestAthenaStatementCompiler filter tests
TestSQLAlchemyAthena::test_conn_str_glue_metadata_fallback REMOVE 2 The URL-to-bool parsing is covered offline for both values and both dialects; passing the attribute to Connection is pyathena.connect's job. TestAthenaDialect::test_conn_str_boolean_options[glue_metadata_fallback-*]
TestSQLAlchemyAthena::test_binary_parameters_and_literals MERGE 4, 2 Put the five values and their literal_execute variants into the one SELECT that test_binary_null_vs_empty runs per driver (saves 50 queries). into TestSQLAlchemyAthena::test_binary_null_vs_empty
TestSQLAlchemyAthena::test_create_s3tables_table_as_select REMOVE 6, 2 It runs raw text() CTAS/SELECT/DROP, which tests Athena; routing to the connection-level catalog is checked by creating and reflecting a table there. TestSQLAlchemyAthena::test_create_s3tables_iceberg_table
TestSQLAlchemyAthena::test_create_table_with_array_types, TestSQLAlchemyAthena::test_create_table_with_map_types, TestSQLAlchemyAthena::test_create_table_with_struct_types, TestSQLAlchemyAthena::test_create_table_with_complex_nested_types REMOVE 2, 4 Substring checks on CreateTable output that the offline DDL tests make with the same or deeper nesting; the engine fixture needs AWS. Add MAP<STRING, ARRAY<STRING>> (from test_create_table_with_map_types) to the first cover. sqlalchemy/test_compiler.py::TestAthenaDDLCompiler::test_create_table_renders_hive_struct_syntax; sqlalchemy/test_array.py::TestArrayTypeInspector::test_array_type_rendering; sqlalchemy/test_compiler.py::TestAthenaTypeCompiler::test_complex_types_render_hive_syntax_without_column
TestSQLAlchemyAthena::test_sqlalchemy_connection_level_callback REMOVE 2, 6 connect_args is SQLAlchemy's pass-through to pyathena.connect, and the connection-level callback is tested at the DB API level. test_cursor.py::TestCursor::test_connection_level_callback

2. sqlalchemy/test_compiler.py

test change criteria reason covered by
TestAthenaTypeCompiler::test_visit_struct_with_fields, TestAthenaTypeCompiler::test_visit_struct_uppercase, TestAthenaTypeCompiler::test_visit_map_default, TestAthenaTypeCompiler::test_visit_map_with_types, TestAthenaTypeCompiler::test_visit_map_uppercase MERGE 2 One assertion per test; fold AthenaStruct(...), STRUCT(...), AthenaMap(), AthenaMap(String, Integer) and MAP(Integer, String) into one parametrized type-to-DDL table. into TestAthenaTypeCompiler::test_complex_types_render_hive_syntax_without_column (or the test_ddl_and_cast_types table)
TestAthenaTypeCompiler::test_visit_struct_single_field REMOVE 2 One-field STRUCT rendering is the two-field case with one field removed. the merged type-to-DDL table above
TestAthenaTypeCompiler::test_visit_struct_empty REMOVE 2 Calling the visitor directly repeats the DDL-level check, which also asserts the column name in the error. TestAthenaDDLCompiler::test_empty_struct_column_raises[type_0]
TestAthenaTypeCompiler::test_visit_struct_no_fields_attribute, TestAthenaTypeCompiler::test_visit_map_no_attributes, TestAthenaTypeCompiler::test_visit_array_no_attributes REMOVE 7 They pass a bare type("Mock...", (), {})() object to visit_* to reach a defensive raise that no public type reaches. —
TestAthenaTypeCompiler::test_visit_array_default, TestAthenaTypeCompiler::test_visit_array_with_type REMOVE 2 Same type-to-DDL cases as the array rendering table, which also checks the DML spelling. sqlalchemy/test_array.py::TestArrayTypeInspector::test_array_type_rendering[AthenaArray()], [types.ARRAY(Integer)]
TestAthenaTypeCompiler::test_visit_array_uppercase REMOVE 2, 5 It accepts ARRAY<STRING> or ARRAY<VARCHAR>, so it passes with the Trino DML spelling in DDL. sqlalchemy/test_array.py::TestArrayTypeInspector::test_array_type_rendering[ARRAY(String(12))]
TestAthenaTypeCompiler::test_visit_json REMOVE 2 Same CompileError for JSON in DDL. TestAthenaTypeCompiler::test_json_is_rejected_in_ddl_and_kept_in_cast[type_0]; TestAthenaDDLCompiler::test_json_column_raises
TestAthenaStatementCompiler::test_visit_filter_func_basic, TestAthenaStatementCompiler::test_visit_filter_func_array_literal, TestAthenaStatementCompiler::test_visit_filter_func_complex_lambda, TestAthenaStatementCompiler::test_visit_filter_func_nested_access, TestAthenaStatementCompiler::test_visit_filter_func_integration_example MERGE 2 Five near copies check that "filter(" and the lambda text appear; merge into one parametrized test that asserts the exact compiled SQL, with an unquoted lambda, for each input. into one parametrized TestAthenaStatementCompiler::test_visit_filter_func
TestAthenaStatementCompiler::test_visit_char_length_func_existing REMOVE 5, 2 "length(" in sql also matches an untranslated char_length(, so it passes without the translation. sqlalchemy/test_base.py::TestSQLAlchemyAthena::test_char_length [original]
TestAthenaStatementCompiler::test_generic_array_slice_step_is_rendered_for_cache_validation REWRITE 1, 8 It calls SQLAlchemy's private compiled._bind_processors[...]; check the step validation by executing through recording_engine or select(...).params(...), and keep the cache-key assertions. —
TestAthenaStatementCompiler::test_difficult_bind_parameter_name REMOVE 2 The compliance suite sends each of these names live as a column name and a bind parameter (commit bb17a66). tests/sqlalchemy/test_suite.py::DifficultParametersTest::test_round_trip_same_named_column
TestAthenaStatementCompiler::test_limit_rendered_multiple_times REMOVE 2 Same contract as the compliance case re-enabled in bb17a66, which passes against Athena. tests/sqlalchemy/test_suite.py::FetchLimitOffsetTest::test_limit_render_multiple_times [original]
TestAthenaStatementCompiler::test_like_pattern_is_not_truncated MERGE 2 The %B%, A%C and A%C%Z cases repeat the live compliance test; reduce the test to the %(x)s case. tests/sqlalchemy/test_suite.py::StringTest::test_dont_truncate_rightside [original]
TestAthenaDDLCompiler::test_create_table_s3tables_table_type_not_iceberg_raises MERGE 2 Same assertion as the non-Iceberg test; parametrize that test's tblproperties over None and {"table_type": "HIVE", "note": "iceberg migration"}. into TestAthenaDDLCompiler::test_create_table_s3tables_non_iceberg_raises
TestAthenaDDLCompiler::test_create_connect_args_stores_connect_options_for_subclass_dialects REWRITE 1 Drop dialect._connect_options["catalog_name"] == ...; assert the DDL that follows (no LOCATION). —

3. sqlalchemy/test_array.py

test change criteria reason covered by
TestAthenaArray::test_creation_with_default, TestAthenaArray::test_creation_with_type_class, TestAthenaArray::test_creation_with_type_instance, TestAthenaArray::test_creation_with_string_type, TestAthenaArray::test_python_type, TestAthenaArray::test_visit_name, TestAthenaArray::test_array_uppercase_visit_name, TestAthenaArray::test_array_with_complex_type, TestAthenaArray::test_array_with_nested_array, TestAthenaArray::test_array_with_map_type REMOVE 6 They assert what the constructor stores, the python_type/__visit_name__ constants, or SQLAlchemy's to_instance. rendering: TestArrayTypeInspector::test_array_type_rendering
TestAthenaArray::test_array_insert_uses_typed_parameter REMOVE 1, 2 It applies SQLAlchemy's private _bind_processors by hand to get CAST(ARRAY[...] AS ARRAY(INTEGER)), which the insertmanyvalues tests produce through a real execute. sqlalchemy/test_base.py::TestAthenaDialect::test_insertmanyvalues_formats_rows; sqlalchemy/test_base.py::TestSQLAlchemyAthena::test_insertmanyvalues
TestArrayValueProcessor::test_array_binding_preserves_in_parameters REMOVE 2 A DefaultParameterFormatter IN-list test that does not involve the SQLAlchemy types. test_formatter.py::TestDefaultParameterFormatter::test_format_int_list
TestArrayWriteIndexType::test_rejects_non_integer_values, TestArrayWriteIndexType::test_bind_and_literal_processors REMOVE 7, 1, 2 They unit-test the private _ArrayWriteIndexType directly (the second one with 2 -> 2). TestArrayUpdateCompiler::test_bound_index_uses_write_index_processor (bind path, after its REWRITE); TestArrayUpdateCompiler::test_invalid_partial_update_index (literal path)
TestAthenaArrayComparator::test_decorated_array_index_and_slice, TestArrayJSONProjection::test_decorated_array_keeps_native_ordering_and_result_processor, TestArrayJSONProjection::test_array_variant_keeps_transport_and_result_types REWRITE 1 Keep the SQL-shape assertions; drop the probes of SQLAlchemy's private compiled._result_columns[0] and of the internal {"_pyathena_array": ...} encoding. result values: tests/sqlalchemy/test_suite.py::ArrayExpressionTest::test_decorated_and_variant_arrays, NativeArrayTest::test_decorated_array_ordering
TestArrayAssignmentType::test_binary_element_assignment_uses_native_hex_parameter, TestArrayAssignmentType::test_explicit_assignment_type_and_callable_value, TestArrayUpdateCompiler::test_bound_index_uses_write_index_processor, TestArrayUpdateCompiler::test_partial_update_compiles_to_one_whole_column_assignment, TestArrayUpdateCompiler::test_write_index_expression_keeps_its_argument_types REWRITE 1, 8 They emulate execution with SQLAlchemy's private compiled._bind_processors; run the statements through recording_engine and assert the recorded, formatted SQL. —

4. sqlalchemy/test_struct.py

test change criteria reason covered by
TestAthenaStruct::test_creation_with_strings, TestAthenaStruct::test_creation_with_tuples, TestAthenaStruct::test_creation_with_type_instances REMOVE 2, 6 The three field-spec forms are covered by the mixed-definition test. TestAthenaStruct::test_mixed_field_definitions
TestAthenaStruct::test_field_access_by_key, TestAthenaStruct::test_field_access_nonexistent_key, TestAthenaStruct::test_python_type, TestAthenaStruct::test_visit_name, TestAthenaStruct::test_struct_uppercase_visit_name, TestAthenaStruct::test_empty_struct REMOVE 6 They check that __getitem__ returns fields[key] (and a dict's KeyError), constants, and that an empty struct has no fields. empty-struct errors: sqlalchemy/test_compiler.py::TestAthenaDDLCompiler::test_empty_struct_column_raises, sqlalchemy/test_compiler.py::TestAthenaStatementCompiler::test_cast_to_empty_struct_raises

5. sqlalchemy/test_map.py

test change criteria reason covered by
TestAthenaMap::test_creation_with_defaults, TestAthenaMap::test_creation_with_type_classes, TestAthenaMap::test_creation_with_type_instances, TestAthenaMap::test_python_type, TestAthenaMap::test_visit_name, TestAthenaMap::test_map_uppercase_visit_name, TestAthenaMap::test_mixed_type_definitions (the whole file) REMOVE 6 They check constructor-stored attributes, constants and SQLAlchemy's to_instance. rendering: sqlalchemy/test_compiler.py MAP rendering tests; reflection: sqlalchemy/test_base.py::TestAthenaDialect::test_top_level_map_and_struct_reflect_their_types

6. sqlalchemy/test_types.py

test change criteria reason covered by
test_double_column_type REMOVE 6, 2 It asserts a module constant (ischema_names["double"] is types.DOUBLE). sqlalchemy/test_base.py::TestSQLAlchemyAthena::test_get_column_type [original] ("double")

7. aio/sqlalchemy/test_base.py

test change criteria reason covered by
TestAthenaAioAdaptedCursor::test_execute_keeps_fetch_error_when_close_fails REWRITE 1 Keep raised.value is error, and replace the exact log-text match with a check that an ERROR record was logged. —
TestAsyncSQLAlchemyAthena::test_binary_parameters_and_literals MERGE 4, 2 Fold the values into the per-driver SELECT, as in the sync row (saves 50 queries); formatting is shared with sync, and only the aio result path differs. into TestAsyncSQLAlchemyAthena::test_binary_null_vs_empty; sqlalchemy/test_base.py::TestSQLAlchemyAthena::test_binary_parameters_and_literals (merged)
TestAsyncSQLAlchemyAthena::test_unicode REMOVE 2 Unicode bind formatting is shared with sync, and aio execution through the adapter is tested on 5 drivers. sqlalchemy/test_base.py::TestSQLAlchemyAthena::test_unicode [original]; TestAsyncSQLAlchemyAthena::test_basic_query
TestAsyncSQLAlchemyAthena::test_get_table_names MERGE 2 The throttled-reflection test calls get_table_names before and after throttling but only compares the two results, so two empty lists pass; assert that its expected contains many_rows. into TestAsyncSQLAlchemyAthena::test_throttled_reflection_reads_glue
TestAsyncSQLAlchemyAthena::test_get_columns REMOVE 2 Same aio get_columns path as reflecting the table, and the field-level assertions repeat the sync test. TestAsyncSQLAlchemyAthena::test_reflect_table; sqlalchemy/test_base.py::TestSQLAlchemyAthena::test_get_columns [original]

Notes

  • Borderline KEEPs:
    • sqlalchemy/test_base.py::TestAthenaDialect::test_compliance_suite_registry_matches_entry_points checks test infrastructure; a drift between tests/sqlalchemy/__init__.py and the entry points makes the compliance suite test the wrong dialects without a failure.
    • TestAthenaDialect::test_conn_str_boolean_options overlaps the original test_conn_str_verify, test_conn_str_kill_on_interrupt and test_conn_str_result_reuse_enable; it is offline and adds the true values, glue_metadata_fallback and the aio dialect.
    • TestAthenaDialect::test_internal_cursor_leaves_out_default_cursor_options calls the private _internal_cursor; it is the regression test for the TypeError with DataFrame-only cursor_kwargs (commit 45639cd).
    • TestAthenaDialect::test_column_lookup_retry_config captures the retry_config kwarg; its [cursor_kwargs] case is the only test of a user retry policy overriding the dialect's (commit 461c0d1).
    • TestAthenaDialect::test_table_level_reflection_of_failed_lookup and ::test_empty_information_schema_result_is_a_missing_table assert query counts and info_cache == {}; they are the federated-catalog and throttling-versus-absence contracts of Read the dialect's own queries through an API cursor and resolve federated absence from information_schema #798.
    • TestSQLAlchemyAthena::test_throttled_reflection_reads_glue (sync and aio) asserts the list of patched client calls; "Glue answered each throttled request once, without retrying" is the contract of Fix metadata reflection throttling and reuse listed metadata #777/Answer throttled metadata requests from Glue in the cursor #803.
    • sqlalchemy/test_compiler.py::TestAthenaDDLCompiler::test_scalar_and_float_column_ddl_is_unchanged is the only test of column-level Float(24) -> FLOAT after the SQLAlchemy type compiler emits invalid DDL for empty STRUCT, JSON, and CLOB columns #886 split.
    • sqlalchemy/test_array.py::TestArrayValueProcessor::test_array_result_conversion probes the internal {"_pyathena_array": ...} wrapper in two lines; the other cases are result-processor contracts.
    • aio/sqlalchemy/test_base.py::TestAthenaAioAdaptedCursor::test_execute_closes_cursor_off_event_loop_when_fetch_fails asserts the thread of close(), which is the documented contract of AthenaAioAdaptedCursor.execute.
  • Unused after the changes:
    • sqlalchemy/test_base.py: the imports DefaultDialect, AthenaAioAdaptedCursor and AthenaTypeCompiler.
    • sqlalchemy/test_array.py: the imports sqltypes and _ArrayWriteIndexType; the class TestArrayWriteIndexType.
    • sqlalchemy/test_map.py: the whole file.
    • sqlalchemy/test_struct.py: the import STRUCT.
    • sqlalchemy/test_types.py: the import ischema_names.
    • sqlalchemy/test_compiler.py: STRUCT and MAP stay in use if the merged table keeps their cases.
    • The _bind_processors REWRITEs use recording_engine; move it from sqlalchemy/test_base.py to tests/pyathena/util.py.
  • Each row's cover is re-checked before the row is applied. A row whose cover does not hold becomes MERGE or KEEP.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions