|
17 | 17 |
|
18 | 18 | """Index update policy uses the identities in a prepared file's own schema.""" |
19 | 19 |
|
| 20 | +from dataclasses import replace |
20 | 21 | from unittest.mock import patch |
21 | 22 |
|
22 | 23 | import pyarrow as pa |
@@ -108,3 +109,50 @@ def test_commit_resolves_updated_index_columns_by_file_schema(native_rest_catalo |
108 | 109 | read = latest.new_read_builder() |
109 | 110 | rows = read.new_read().to_arrow(read.new_scan().plan().splits()).to_pydict() |
110 | 111 | assert rows == {'id': [1], index_field.name: [None if evolution == 'drop_readd' else 11]} |
| 112 | + |
| 113 | + |
| 114 | +@pytest.mark.parametrize('native', _ENGINES) |
| 115 | +@pytest.mark.parametrize('action', ['THROW_ERROR', 'DROP_PARTITION_INDEX', 'IGNORE']) |
| 116 | +@pytest.mark.parametrize('deleted', ['affected', 'other', 'both']) |
| 117 | +def test_explicit_index_deletion_does_not_exempt_other_indexes(native_rest_catalog, native, action, deleted): |
| 118 | + catalog = native_rest_catalog |
| 119 | + schema = pa.schema([('id', pa.int32()), ('value', pa.int32())]) |
| 120 | + catalog.create_table('default.index_deletes', Schema.from_pyarrow_schema(schema, options={ |
| 121 | + 'data-evolution.enabled': 'true', 'row-tracking.enabled': 'true', |
| 122 | + 'write.native.enabled': 'false', 'commit.native.enabled': 'false', |
| 123 | + 'read.native.enabled': 'false', 'scan.native-plan.enabled': 'false', |
| 124 | + 'global-index.column-update-action': action, |
| 125 | + }), False) |
| 126 | + table = catalog.get_table('default.index_deletes') |
| 127 | + writer = table.new_batch_write_builder().new_write() |
| 128 | + try: |
| 129 | + writer.write_arrow(pa.table({'id': [1], 'value': [10]}, schema=schema)) |
| 130 | + _commit(table, writer.prepare_commit()) |
| 131 | + finally: |
| 132 | + writer.close() |
| 133 | + affected = _index_entry('affected.index', (), table.fields[1].id) |
| 134 | + other = _index_entry('other.index', (), table.fields[0].id) |
| 135 | + _commit(table, [CommitMessage((), 0, [], index_adds=[affected, other])]) |
| 136 | + before = table.snapshot_manager().get_latest_snapshot() |
| 137 | + source = table.copy({'global-index.column-update-action': 'IGNORE'}) |
| 138 | + messages = source.new_batch_write_builder().new_update().new_update_by_row_id().update_columns( |
| 139 | + pa.table({'_ROW_ID': [0], 'value': [11]}), ['value']) |
| 140 | + deletions = [affected] if deleted == 'affected' else [other] if deleted == 'other' else [affected, other] |
| 141 | + messages.append(CommitMessage((), 0, [], index_deletes=[replace(entry, kind=1) for entry in deletions])) |
| 142 | + target = table.copy({'commit.native.enabled': str(native).lower()}) |
| 143 | + if action == 'THROW_ERROR' and deleted == 'other': |
| 144 | + with pytest.raises(Exception, match='globally indexed columns'): |
| 145 | + _commit(target, messages, native) |
| 146 | + assert table.snapshot_manager().get_latest_snapshot().id == before.id |
| 147 | + expected_indexes, expected_value = ['affected.index', 'other.index'], 10 |
| 148 | + else: |
| 149 | + _commit(target, messages, native) |
| 150 | + expected_indexes = ['other.index'] if deleted == 'affected' else ( |
| 151 | + ['affected.index'] if deleted == 'other' and action == 'IGNORE' else []) |
| 152 | + expected_value = 11 |
| 153 | + snapshot = table.snapshot_manager().get_latest_snapshot() |
| 154 | + actual_indexes = sorted(entry.index_file.file_name for entry in scan_global_index_entries(table, snapshot)) |
| 155 | + assert actual_indexes == expected_indexes |
| 156 | + read = table.new_read_builder() |
| 157 | + assert read.new_read().to_arrow(read.new_scan().plan().splits()).to_pydict() == { |
| 158 | + 'id': [1], 'value': [expected_value]} |
0 commit comments