sqlmesh 0.186.2.dev18__py3-none-any.whl → 0.186.2.dev20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sqlmesh/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.186.2.dev18'
21
- __version_tuple__ = version_tuple = (0, 186, 2, 'dev18')
20
+ __version__ = version = '0.186.2.dev20'
21
+ __version_tuple__ = version_tuple = (0, 186, 2, 'dev20')
sqlmesh/core/context.py CHANGED
@@ -1129,33 +1129,15 @@ class GenericContext(BaseContext, t.Generic[C]):
1129
1129
 
1130
1130
  with open(target._path, "r+", encoding="utf-8") as file:
1131
1131
  before = file.read()
1132
- expressions = parse(before, default_dialect=self.config_for_node(target).dialect)
1133
- if transpile and is_meta_expression(expressions[0]):
1134
- for prop in expressions[0].expressions:
1135
- if prop.name.lower() == "dialect":
1136
- prop.replace(
1137
- exp.Property(
1138
- this="dialect",
1139
- value=exp.Literal.string(transpile or target.dialect),
1140
- )
1141
- )
1142
-
1143
- format_config = self.config_for_node(target).format
1144
- after = format_model_expressions(
1145
- expressions,
1146
- transpile or target.dialect,
1147
- rewrite_casts=(
1148
- rewrite_casts
1149
- if rewrite_casts is not None
1150
- else not format_config.no_rewrite_casts
1151
- ),
1152
- **{**format_config.generator_options, **kwargs},
1153
- )
1154
1132
 
1155
- if append_newline is None:
1156
- append_newline = format_config.append_newline
1157
- if append_newline:
1158
- after += "\n"
1133
+ after = self._format(
1134
+ target,
1135
+ before,
1136
+ transpile=transpile,
1137
+ rewrite_casts=rewrite_casts,
1138
+ append_newline=append_newline,
1139
+ **kwargs,
1140
+ )
1159
1141
 
1160
1142
  if not check:
1161
1143
  file.seek(0)
@@ -1174,6 +1156,44 @@ class GenericContext(BaseContext, t.Generic[C]):
1174
1156
 
1175
1157
  return True
1176
1158
 
1159
+ def _format(
1160
+ self,
1161
+ target: Model | Audit,
1162
+ before: str,
1163
+ *,
1164
+ transpile: t.Optional[str] = None,
1165
+ rewrite_casts: t.Optional[bool] = None,
1166
+ append_newline: t.Optional[bool] = None,
1167
+ **kwargs: t.Any,
1168
+ ) -> str:
1169
+ expressions = parse(before, default_dialect=self.config_for_node(target).dialect)
1170
+ if transpile and is_meta_expression(expressions[0]):
1171
+ for prop in expressions[0].expressions:
1172
+ if prop.name.lower() == "dialect":
1173
+ prop.replace(
1174
+ exp.Property(
1175
+ this="dialect",
1176
+ value=exp.Literal.string(transpile or target.dialect),
1177
+ )
1178
+ )
1179
+
1180
+ format_config = self.config_for_node(target).format
1181
+ after = format_model_expressions(
1182
+ expressions,
1183
+ transpile or target.dialect,
1184
+ rewrite_casts=(
1185
+ rewrite_casts if rewrite_casts is not None else not format_config.no_rewrite_casts
1186
+ ),
1187
+ **{**format_config.generator_options, **kwargs},
1188
+ )
1189
+
1190
+ if append_newline is None:
1191
+ append_newline = format_config.append_newline
1192
+ if append_newline:
1193
+ after += "\n"
1194
+
1195
+ return after
1196
+
1177
1197
  @python_api_analytics
1178
1198
  def plan(
1179
1199
  self,
sqlmesh/lsp/main.py CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env python
2
2
  """A Language Server Protocol (LSP) server for SQL with SQLMesh integration, refactored without globals."""
3
3
 
4
+ from itertools import chain
4
5
  import logging
5
6
  import typing as t
6
7
  from pathlib import Path
@@ -20,7 +21,11 @@ from sqlmesh.lsp.api import (
20
21
  ApiResponseGetModels,
21
22
  )
22
23
  from sqlmesh.lsp.completions import get_sql_completions
23
- from sqlmesh.lsp.context import LSPContext, ModelTarget, render_model as render_model_context
24
+ from sqlmesh.lsp.context import (
25
+ LSPContext,
26
+ ModelTarget,
27
+ render_model as render_model_context,
28
+ )
24
29
  from sqlmesh.lsp.custom import (
25
30
  ALL_MODELS_FEATURE,
26
31
  RENDER_MODEL_FEATURE,
@@ -213,15 +218,29 @@ class SQLMeshLanguageServer:
213
218
  uri = URI(params.text_document.uri)
214
219
  self._ensure_context_for_document(uri)
215
220
  document = ls.workspace.get_text_document(params.text_document.uri)
221
+ before = document.source
216
222
  if self.lsp_context is None:
217
223
  raise RuntimeError(f"No context found for document: {document.path}")
218
224
 
219
- # Perform formatting using the loaded context
220
- self.lsp_context.context.format(paths=(str(uri.to_path()),))
221
- with open(uri.to_path(), "r+", encoding="utf-8") as file:
222
- new_text = file.read()
223
-
224
- # Return a single edit that replaces the entire file.
225
+ target = next(
226
+ (
227
+ target
228
+ for target in chain(
229
+ self.lsp_context.context._models.values(),
230
+ self.lsp_context.context._audits.values(),
231
+ )
232
+ if target._path is not None
233
+ and target._path.suffix == ".sql"
234
+ and (target._path.samefile(uri.to_path()))
235
+ ),
236
+ None,
237
+ )
238
+ if target is None:
239
+ return []
240
+ after = self.lsp_context.context._format(
241
+ target=target,
242
+ before=before,
243
+ )
225
244
  return [
226
245
  types.TextEdit(
227
246
  range=types.Range(
@@ -231,7 +250,7 @@ class SQLMeshLanguageServer:
231
250
  character=len(document.lines[-1]) if document.lines else 0,
232
251
  ),
233
252
  ),
234
- new_text=new_text,
253
+ new_text=after,
235
254
  )
236
255
  ]
237
256
  except Exception as e:
@@ -279,21 +298,31 @@ class SQLMeshLanguageServer:
279
298
  raise RuntimeError(f"No context found for document: {document.path}")
280
299
 
281
300
  references = get_references(self.lsp_context, uri, params.position)
282
- return [
283
- types.LocationLink(
284
- target_uri=reference.uri,
285
- target_selection_range=types.Range(
301
+ location_links = []
302
+ for reference in references:
303
+ # Use target_range if available (for CTEs), otherwise default to start of file
304
+ if reference.target_range:
305
+ target_range = reference.target_range
306
+ target_selection_range = reference.target_range
307
+ else:
308
+ target_range = types.Range(
286
309
  start=types.Position(line=0, character=0),
287
310
  end=types.Position(line=0, character=0),
288
- ),
289
- target_range=types.Range(
311
+ )
312
+ target_selection_range = types.Range(
290
313
  start=types.Position(line=0, character=0),
291
314
  end=types.Position(line=0, character=0),
292
- ),
293
- origin_selection_range=reference.range,
315
+ )
316
+
317
+ location_links.append(
318
+ types.LocationLink(
319
+ target_uri=reference.uri,
320
+ target_selection_range=target_selection_range,
321
+ target_range=target_range,
322
+ origin_selection_range=reference.range,
323
+ )
294
324
  )
295
- for reference in references
296
- ]
325
+ return location_links
297
326
  except Exception as e:
298
327
  ls.show_message(f"Error getting references: {e}", types.MessageType.Error)
299
328
  return []
sqlmesh/lsp/reference.py CHANGED
@@ -6,23 +6,27 @@ from sqlmesh.core.model.definition import SqlModel
6
6
  from sqlmesh.lsp.context import LSPContext, ModelTarget, AuditTarget
7
7
  from sqlglot import exp
8
8
  from sqlmesh.lsp.description import generate_markdown_description
9
+ from sqlglot.optimizer.scope import build_scope
9
10
  from sqlmesh.lsp.uri import URI
10
11
  from sqlmesh.utils.pydantic import PydanticModel
12
+ from sqlglot.optimizer.normalize_identifiers import normalize_identifiers
11
13
 
12
14
 
13
15
  class Reference(PydanticModel):
14
16
  """
15
- A reference to a model.
17
+ A reference to a model or CTE.
16
18
 
17
19
  Attributes:
18
20
  range: The range of the reference in the source file
19
21
  uri: The uri of the referenced model
20
22
  markdown_description: The markdown description of the referenced model
23
+ target_range: The range of the definition for go-to-definition (optional, used for CTEs)
21
24
  """
22
25
 
23
26
  range: Range
24
27
  uri: str
25
28
  markdown_description: t.Optional[str] = None
29
+ target_range: t.Optional[Range] = None
26
30
 
27
31
 
28
32
  def by_position(position: Position) -> t.Callable[[Reference], bool]:
@@ -88,6 +92,7 @@ def get_model_definitions_for_a_path(
88
92
  - Need to normalize it before matching
89
93
  - Try get_model before normalization
90
94
  - Match to models that the model refers to
95
+ - Also find CTE references within the query
91
96
  """
92
97
  path = document_uri.to_path()
93
98
  if path.suffix != ".sql":
@@ -126,66 +131,95 @@ def get_model_definitions_for_a_path(
126
131
  # Find all possible references
127
132
  references = []
128
133
 
129
- # Get SQL query and find all table references
130
- tables = list(query.find_all(exp.Table))
131
- if len(tables) == 0:
132
- return []
133
-
134
134
  with open(file_path, "r", encoding="utf-8") as file:
135
135
  read_file = file.readlines()
136
136
 
137
- for table in tables:
138
- # Normalize the table reference
139
- unaliased = table.copy()
140
- if unaliased.args.get("alias") is not None:
141
- unaliased.set("alias", None)
142
- reference_name = unaliased.sql(dialect=dialect)
143
- try:
144
- normalized_reference_name = normalize_model_name(
145
- reference_name,
146
- default_catalog=lint_context.context.default_catalog,
147
- dialect=dialect,
148
- )
149
- if normalized_reference_name not in depends_on:
150
- continue
151
- except Exception:
152
- # Skip references that cannot be normalized
153
- continue
154
-
155
- # Get the referenced model uri
156
- referenced_model = lint_context.context.get_model(
157
- model_or_snapshot=normalized_reference_name, raise_if_missing=False
158
- )
159
- if referenced_model is None:
160
- continue
161
- referenced_model_path = referenced_model._path
162
- # Check whether the path exists
163
- if not referenced_model_path.is_file():
164
- continue
165
- referenced_model_uri = URI.from_path(referenced_model_path)
166
-
167
- # Extract metadata for positioning
168
- table_meta = TokenPositionDetails.from_meta(table.this.meta)
169
- table_range = _range_from_token_position_details(table_meta, read_file)
170
- start_pos = table_range.start
171
- end_pos = table_range.end
172
-
173
- # If there's a catalog or database qualifier, adjust the start position
174
- catalog_or_db = table.args.get("catalog") or table.args.get("db")
175
- if catalog_or_db is not None:
176
- catalog_or_db_meta = TokenPositionDetails.from_meta(catalog_or_db.meta)
177
- catalog_or_db_range = _range_from_token_position_details(catalog_or_db_meta, read_file)
178
- start_pos = catalog_or_db_range.start
179
-
180
- description = generate_markdown_description(referenced_model)
181
-
182
- references.append(
183
- Reference(
184
- uri=referenced_model_uri.value,
185
- range=Range(start=start_pos, end=end_pos),
186
- markdown_description=description,
187
- )
188
- )
137
+ # Build scope tree to properly handle nested CTEs
138
+ query = normalize_identifiers(query.copy(), dialect=dialect)
139
+ root_scope = build_scope(query)
140
+
141
+ if root_scope:
142
+ # Traverse all scopes to find CTE definitions and table references
143
+ for scope in root_scope.traverse():
144
+ for table in scope.tables:
145
+ table_name = table.name
146
+
147
+ # Check if this table reference is a CTE in the current scope
148
+ if cte_scope := scope.cte_sources.get(table_name):
149
+ cte = cte_scope.expression.parent
150
+ alias = cte.args["alias"]
151
+ if isinstance(alias, exp.TableAlias):
152
+ identifier = alias.this
153
+ if isinstance(identifier, exp.Identifier):
154
+ target_range = _range_from_token_position_details(
155
+ TokenPositionDetails.from_meta(identifier.meta), read_file
156
+ )
157
+ table_range = _range_from_token_position_details(
158
+ TokenPositionDetails.from_meta(table.this.meta), read_file
159
+ )
160
+ references.append(
161
+ Reference(
162
+ uri=document_uri.value, # Same file
163
+ range=table_range,
164
+ target_range=target_range,
165
+ )
166
+ )
167
+ continue
168
+
169
+ # For non-CTE tables, process as before (external model references)
170
+ # Normalize the table reference
171
+ unaliased = table.copy()
172
+ if unaliased.args.get("alias") is not None:
173
+ unaliased.set("alias", None)
174
+ reference_name = unaliased.sql(dialect=dialect)
175
+ try:
176
+ normalized_reference_name = normalize_model_name(
177
+ reference_name,
178
+ default_catalog=lint_context.context.default_catalog,
179
+ dialect=dialect,
180
+ )
181
+ if normalized_reference_name not in depends_on:
182
+ continue
183
+ except Exception:
184
+ # Skip references that cannot be normalized
185
+ continue
186
+
187
+ # Get the referenced model uri
188
+ referenced_model = lint_context.context.get_model(
189
+ model_or_snapshot=normalized_reference_name, raise_if_missing=False
190
+ )
191
+ if referenced_model is None:
192
+ continue
193
+ referenced_model_path = referenced_model._path
194
+ # Check whether the path exists
195
+ if not referenced_model_path.is_file():
196
+ continue
197
+ referenced_model_uri = URI.from_path(referenced_model_path)
198
+
199
+ # Extract metadata for positioning
200
+ table_meta = TokenPositionDetails.from_meta(table.this.meta)
201
+ table_range = _range_from_token_position_details(table_meta, read_file)
202
+ start_pos = table_range.start
203
+ end_pos = table_range.end
204
+
205
+ # If there's a catalog or database qualifier, adjust the start position
206
+ catalog_or_db = table.args.get("catalog") or table.args.get("db")
207
+ if catalog_or_db is not None:
208
+ catalog_or_db_meta = TokenPositionDetails.from_meta(catalog_or_db.meta)
209
+ catalog_or_db_range = _range_from_token_position_details(
210
+ catalog_or_db_meta, read_file
211
+ )
212
+ start_pos = catalog_or_db_range.start
213
+
214
+ description = generate_markdown_description(referenced_model)
215
+
216
+ references.append(
217
+ Reference(
218
+ uri=referenced_model_uri.value,
219
+ range=Range(start=start_pos, end=end_pos),
220
+ markdown_description=description,
221
+ )
222
+ )
189
223
 
190
224
  return references
191
225
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlmesh
3
- Version: 0.186.2.dev18
3
+ Version: 0.186.2.dev20
4
4
  Summary: Next-generation data transformation framework
5
5
  Author-email: "TobikoData Inc." <engineering@tobikodata.com>
6
6
  License: Apache License
@@ -1,5 +1,5 @@
1
1
  sqlmesh/__init__.py,sha256=69xw6bTmi4cW90M-sl3g6wLDgxVcLHcKg1q8GS3lqyA,7466
2
- sqlmesh/_version.py,sha256=23uEi0q4YZ9-xEEuuYzX2qYiIPhDRAIz-zuV9UYt5Ng,530
2
+ sqlmesh/_version.py,sha256=md758L-6fCOTdjqRZ-XWWVl4pSKbkoIiqBeQD-HssQk,530
3
3
  sqlmesh/magics.py,sha256=JHorAZNtezf123jmTyNSoUC9yHmv-08hMP205PJmmIo,39750
4
4
  sqlmesh/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  sqlmesh/cicd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -13,7 +13,7 @@ sqlmesh/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  sqlmesh/core/_typing.py,sha256=e1f7xO2aZCy38M_3VoDy-XI3shyd99-IFctgIFSgXWM,497
14
14
  sqlmesh/core/console.py,sha256=ZAaGLOWBJGBxPzLV7KZZ9akPExgD5m5AboCM8O0Q0nY,150944
15
15
  sqlmesh/core/constants.py,sha256=oqt6zjepUhQoWlgapKn_HpG7Pu9mrFwepdX5caNfJUI,2499
16
- sqlmesh/core/context.py,sha256=paXJMdaydEQ9Tp80fVUnxveBlbCN7kGGEvesqAiezmw,115276
16
+ sqlmesh/core/context.py,sha256=gOwToFkXSQjb71Ki3cEqfxYjS64p2T2XKfZPVBtjGQA,115624
17
17
  sqlmesh/core/context_diff.py,sha256=s9toP17d4tJfOpFH91LcjGK2CM64PVuB0MPkeFgF4hQ,20415
18
18
  sqlmesh/core/dialect.py,sha256=fPTV517IOS0nWMxYznNNQnXkQkFFrPOSKe8ghsQ0o8A,49759
19
19
  sqlmesh/core/environment.py,sha256=0TTQf4C4LXwf37fm-dfYv1qyG7vtZfQI5rizgm5g0j0,11442
@@ -160,8 +160,8 @@ sqlmesh/lsp/completions.py,sha256=Uo8lUzPBeoB58Eu2642YoAtNI0pgIFWFSvwmuJALTv4,35
160
160
  sqlmesh/lsp/context.py,sha256=nCWg2uVH28-IJC2uDYuCbiXTh1lUFElSpNu4Q-fDWi4,2918
161
161
  sqlmesh/lsp/custom.py,sha256=1YVcQz0y-4cdsvTdOQvlhUQXwOqPBWi3v_u1X2EqQxE,901
162
162
  sqlmesh/lsp/description.py,sha256=4QIGC9ix8IzmWF6DVUNhY4m1Crk3sNQA6hZ1RbyqU78,827
163
- sqlmesh/lsp/main.py,sha256=5EF1xA1rESYq4sTKDJPPG9Zl6mx3xfy6dbzWBbDee0M,18553
164
- sqlmesh/lsp/reference.py,sha256=bO2edkI2avmEYayQI11_OsDX1gjp36kq9ANwHczWSC0,8285
163
+ sqlmesh/lsp/main.py,sha256=s8e9mD-J6pbxCrEZiMlRL0DvUu1rD3vxVdebgx5kjhA,19609
164
+ sqlmesh/lsp/reference.py,sha256=v4VXMYQY9rPC8b5w-jzob3i77VdZ1FZ8sA_-sWuPx7M,10502
165
165
  sqlmesh/lsp/uri.py,sha256=mvSLbZGmMTHPpliThOtVWiFcHnfJzp1w39q9l-xD2SI,802
166
166
  sqlmesh/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
167
167
  sqlmesh/migrations/v0001_init.py,sha256=h8FwMYJj9_O8yGpk12guaSaGwX5A3we2GiKeHrXi15o,2048
@@ -270,7 +270,7 @@ sqlmesh/utils/pydantic.py,sha256=I898jAFqa3qU6lG2uPpRebVFncOUo8ZrLWM_R3K3DHU,116
270
270
  sqlmesh/utils/rich.py,sha256=xNZyPCqEc4TAlJlsEthg-5eYUWpaizz0QjBzO7a1Yv4,1509
271
271
  sqlmesh/utils/windows.py,sha256=r7Yin4WEwbvH7sKA3hOJtRsclmscNuBeCklCfVPvtQs,611
272
272
  sqlmesh/utils/yaml.py,sha256=np-WVcwmwsKghCgAj1OBBMsK1SXSRn4wR-H37cGiFr8,2282
273
- sqlmesh-0.186.2.dev18.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
273
+ sqlmesh-0.186.2.dev20.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
274
274
  web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
275
275
  web/client/dist/index.html,sha256=M50OuwjW63dRCS0Dz40Yecgvw9ecLFqizbTa6gv5Nd4,5472
276
276
  web/client/dist/assets/Audits-BPlxzqeG.js,sha256=TCx1qCjCGgmUSvH6HoPr5t8ztbRXMlOxrcW4o7hRjlA,1041
@@ -388,8 +388,8 @@ web/server/api/endpoints/models.py,sha256=ud-lRwriyI_OYg9tZchrBgeiXELiRTLNEodraf
388
388
  web/server/api/endpoints/modules.py,sha256=8hqqgonGay_mJmpCw0IdbjsPhWlQH2VLdKAqha-myac,468
389
389
  web/server/api/endpoints/plan.py,sha256=OYuQ_4dgbhG9ibTcX1a8mvYgH3ANxw9Fhbb4wg6QWqc,9222
390
390
  web/server/api/endpoints/table_diff.py,sha256=Wd5YxeKbZB7AkrDvqFgvV9ZQlnKYOF1n5W_w2EHE_Zw,2085
391
- sqlmesh-0.186.2.dev18.dist-info/METADATA,sha256=zjC2hE6KzopYHThETS-RzPimIwcnBi-IBr4Ogt_7rB4,25524
392
- sqlmesh-0.186.2.dev18.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
393
- sqlmesh-0.186.2.dev18.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
394
- sqlmesh-0.186.2.dev18.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
395
- sqlmesh-0.186.2.dev18.dist-info/RECORD,,
391
+ sqlmesh-0.186.2.dev20.dist-info/METADATA,sha256=7njLBJTTA1FSif3Pvs0NSw7SoMzwz3DzupTHsU1ZS40,25524
392
+ sqlmesh-0.186.2.dev20.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
393
+ sqlmesh-0.186.2.dev20.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
394
+ sqlmesh-0.186.2.dev20.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
395
+ sqlmesh-0.186.2.dev20.dist-info/RECORD,,