|
| 1 | +from pathlib import Path |
| 2 | + |
1 | 3 | from lsprotocol.types import Position |
| 4 | +from sqlmesh.cli.example_project import init_example_project |
2 | 5 | from sqlmesh.core.context import Context |
3 | 6 | from sqlmesh.lsp.context import LSPContext, ModelTarget |
4 | 7 | from sqlmesh.lsp.reference import get_all_references |
@@ -77,3 +80,133 @@ def test_column_prefix_references_are_found(): |
77 | 80 | assert any("WHERE sushi.orders.customer_id" in content for content in line_contents), ( |
78 | 81 | "Should find WHERE clause with sushi.orders.customer_id" |
79 | 82 | ) |
| 83 | + |
| 84 | + |
| 85 | +def test_quoted_uppercase_table_and_column_references(tmp_path: Path): |
| 86 | + # Initialize example project in temporary directory with case sensitive normalization |
| 87 | + init_example_project(tmp_path, dialect="duckdb,normalization_strategy=case_sensitive") |
| 88 | + |
| 89 | + # Create a model with quoted uppercase schema and table names |
| 90 | + models_dir = tmp_path / "models" |
| 91 | + |
| 92 | + # First, create the uppercase SUSHI.orders model that will be referenced |
| 93 | + uppercase_orders_path = models_dir / "uppercase_orders.sql" |
| 94 | + uppercase_orders_path.write_text("""MODEL ( |
| 95 | + name "SUSHI".orders, |
| 96 | + kind FULL |
| 97 | +); |
| 98 | +
|
| 99 | +SELECT |
| 100 | + 1 as id, |
| 101 | + 1 as customer_id, |
| 102 | + 1 as item_id""") |
| 103 | + |
| 104 | + # Second, create the lowercase sushi.orders model that will be referenced |
| 105 | + lowercase_orders_path = models_dir / "lowercase_orders.sql" |
| 106 | + lowercase_orders_path.write_text("""MODEL ( |
| 107 | + name sushi.orders, |
| 108 | + kind FULL |
| 109 | +); |
| 110 | +
|
| 111 | +SELECT |
| 112 | + 1 as id, |
| 113 | + 1 as customer_id""") |
| 114 | + |
| 115 | + quoted_test_path = models_dir / "quoted_test.sql" |
| 116 | + quoted_test_path.write_text("""MODEL ( |
| 117 | + name "SUSHI".quoted_test, |
| 118 | + kind FULL |
| 119 | +); |
| 120 | +
|
| 121 | +SELECT |
| 122 | + o.id, |
| 123 | + o.customer_id, |
| 124 | + o.item_id, |
| 125 | + c.item_id as c_item_id |
| 126 | +FROM "SUSHI".orders AS o, sushi.orders as c |
| 127 | +WHERE "SUSHI".orders.id > 0 |
| 128 | + AND "SUSHI".orders.customer_id IS NOT NULL |
| 129 | + AND sushi.orders.id > 0""") |
| 130 | + |
| 131 | + context = Context(paths=tmp_path) |
| 132 | + lsp_context = LSPContext(context) |
| 133 | + |
| 134 | + # Find the quoted test model |
| 135 | + quoted_test_model_path = next( |
| 136 | + path |
| 137 | + for path, info in lsp_context.map.items() |
| 138 | + if isinstance(info, ModelTarget) and '"SUSHI".quoted_test' in info.names |
| 139 | + ) |
| 140 | + |
| 141 | + with open(quoted_test_model_path, "r", encoding="utf-8") as file: |
| 142 | + read_file = file.readlines() |
| 143 | + |
| 144 | + # Test finding references for quoted "SUSHI".orders |
| 145 | + ranges = find_ranges_from_regex(read_file, r'"SUSHI"\.orders') |
| 146 | + |
| 147 | + # Should find 3 occurrences: FROM clause and 2 in WHERE clause with column prefix |
| 148 | + assert len(ranges) == 3, f"Expected 3 occurrences of '\"SUSHI\".orders', found {len(ranges)}" |
| 149 | + |
| 150 | + # Click on the table reference in FROM clause |
| 151 | + from_clause_range = None |
| 152 | + for r in ranges: |
| 153 | + line_content = read_file[r.start.line].strip() |
| 154 | + if "FROM" in line_content: |
| 155 | + from_clause_range = r |
| 156 | + break |
| 157 | + |
| 158 | + assert from_clause_range is not None, 'Should find FROM clause with "SUSHI".orders' |
| 159 | + |
| 160 | + position = Position( |
| 161 | + line=from_clause_range.start.line, character=from_clause_range.start.character + 5 |
| 162 | + ) |
| 163 | + |
| 164 | + model_refs = get_all_references(lsp_context, URI.from_path(quoted_test_model_path), position) |
| 165 | + |
| 166 | + # Should find only references to "SUSHI".orders (3 total: FROM clause and 2 column prefixes in WHERE) |
| 167 | + # The lowercase sushi.orders should NOT be included if case sensitivity is working |
| 168 | + assert len(model_refs) == 4, ( |
| 169 | + f'Expected exactly 3 references for "SUSHI".orders, found {len(model_refs)}' |
| 170 | + ) |
| 171 | + |
| 172 | + # Verify that we have all 3 references |
| 173 | + ref_lines = [ref.range.start.line for ref in model_refs] |
| 174 | + |
| 175 | + # Count how many references are on each line |
| 176 | + from_line = from_clause_range.start.line |
| 177 | + where_lines = [r.start.line for r in ranges if r.start.line != from_line] |
| 178 | + |
| 179 | + assert from_line in ref_lines, "Should find FROM clause reference" |
| 180 | + for where_line in where_lines: |
| 181 | + assert where_line in ref_lines, f"Should find WHERE clause reference on line {where_line}" |
| 182 | + |
| 183 | + # Now test that lowercase sushi.orders references are separate |
| 184 | + lowercase_ranges = find_ranges_from_regex(read_file, r"sushi\.orders") |
| 185 | + |
| 186 | + # Should find 2 occurrences: FROM clause and 1 in WHERE clause |
| 187 | + assert len(lowercase_ranges) == 2, ( |
| 188 | + f"Expected 2 occurrences of 'sushi.orders', found {len(lowercase_ranges)}" |
| 189 | + ) |
| 190 | + |
| 191 | + # Click on the lowercase table reference |
| 192 | + lowercase_from_range = None |
| 193 | + for r in lowercase_ranges: |
| 194 | + line_content = read_file[r.start.line].strip() |
| 195 | + if "FROM" in line_content: |
| 196 | + lowercase_from_range = r |
| 197 | + break |
| 198 | + |
| 199 | + assert lowercase_from_range is not None, "Should find FROM clause with sushi.orders" |
| 200 | + |
| 201 | + lowercase_position = Position( |
| 202 | + line=lowercase_from_range.start.line, character=lowercase_from_range.start.character + 5 |
| 203 | + ) |
| 204 | + |
| 205 | + lowercase_refs = get_all_references( |
| 206 | + lsp_context, URI.from_path(quoted_test_model_path), lowercase_position |
| 207 | + ) |
| 208 | + |
| 209 | + # Should find only references to lowercase sushi.orders, NOT the uppercase ones |
| 210 | + assert len(lowercase_refs) == 3, ( |
| 211 | + f"Expected exactly 2 references for sushi.orders, found {len(lowercase_refs)}" |
| 212 | + ) |
0 commit comments