Repository navigation
Remove and rewrite unnecessary tests in the SQLAlchemy dialect tests #1242
Description
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
- No open PR (Move S3File writes onto a multipart write session #1232, Read UNLOAD timestamps at microseconds #1233, Break result sets after failed or cancelled fetches and drop the cross-thread guards #1238) changes these files. Read CSV dates as date32 and temporal text NULL as null in ArrowCursor #1236, merged on master at 7e5d9ee, does not change them either.
- Section 1,
TestSQLAlchemyAthena::test_select_nested_struct_queryand::test_select_array_with_nested_struct: one cover,test_cursor.py::TestCursor::test_complex_with_type_hints, is a REWRITE in the top-level sub-issue. That rewrite keeps the three hinted expressions in one query, so the removal can land before or after it. - Within this sub-issue, these removals land with or after the change that keeps their cover:
TestAthenaDialect::test_get_view_definition_keeps_blank_linesafter the reduction ofTestSQLAlchemyAthena::test_get_view_definition_across_cursor_types(the kept case still checks the blank lines).TestAthenaTypeCompiler::test_visit_struct_single_fieldafter the MERGE of thetest_visit_struct_*/test_visit_map_*tests.TestArrayWriteIndexType::*after the REWRITE ofTestArrayUpdateCompiler::test_bound_index_uses_write_index_processor.- The aio
test_binary_parameters_and_literalsMERGE with the sync MERGE of the same name.
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_pointschecks test infrastructure; a drift betweentests/sqlalchemy/__init__.pyand the entry points makes the compliance suite test the wrong dialects without a failure.TestAthenaDialect::test_conn_str_boolean_optionsoverlaps the originaltest_conn_str_verify,test_conn_str_kill_on_interruptandtest_conn_str_result_reuse_enable; it is offline and adds thetruevalues,glue_metadata_fallbackand the aio dialect.TestAthenaDialect::test_internal_cursor_leaves_out_default_cursor_optionscalls the private_internal_cursor; it is the regression test for the TypeError with DataFrame-onlycursor_kwargs(commit 45639cd).TestAthenaDialect::test_column_lookup_retry_configcaptures theretry_configkwarg; 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_lookupand::test_empty_information_schema_result_is_a_missing_tableassert query counts andinfo_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_unchangedis the only test of column-levelFloat(24) -> FLOATafter the SQLAlchemy type compiler emits invalid DDL for empty STRUCT, JSON, and CLOB columns #886 split.sqlalchemy/test_array.py::TestArrayValueProcessor::test_array_result_conversionprobes 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_failsasserts the thread ofclose(), which is the documented contract ofAthenaAioAdaptedCursor.execute.
- Unused after the changes:
sqlalchemy/test_base.py: the importsDefaultDialect,AthenaAioAdaptedCursorandAthenaTypeCompiler.sqlalchemy/test_array.py: the importssqltypesand_ArrayWriteIndexType; the classTestArrayWriteIndexType.sqlalchemy/test_map.py: the whole file.sqlalchemy/test_struct.py: the importSTRUCT.sqlalchemy/test_types.py: the importischema_names.sqlalchemy/test_compiler.py:STRUCTandMAPstay in use if the merged table keeps their cases.- The
_bind_processorsREWRITEs userecording_engine; move it fromsqlalchemy/test_base.pytotests/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.
Activity
Restore request and result checks in the SQLAlchemy offline tests
Test failed pings, catalog-cased caches, and the retry override offline