sqlmesh 0.209.1.dev12__py3-none-any.whl → 0.209.1.dev13__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 +2 -2
- sqlmesh/core/audit/definition.py +5 -3
- sqlmesh/core/constants.py +2 -0
- sqlmesh/core/macros.py +16 -3
- sqlmesh/core/model/common.py +198 -41
- sqlmesh/core/model/definition.py +18 -11
- sqlmesh/migrations/v0088_warn_about_variable_python_env_diffs.py +74 -0
- sqlmesh/utils/jinja.py +1 -1
- sqlmesh/utils/metaprogramming.py +12 -5
- {sqlmesh-0.209.1.dev12.dist-info → sqlmesh-0.209.1.dev13.dist-info}/METADATA +1 -1
- {sqlmesh-0.209.1.dev12.dist-info → sqlmesh-0.209.1.dev13.dist-info}/RECORD +15 -14
- {sqlmesh-0.209.1.dev12.dist-info → sqlmesh-0.209.1.dev13.dist-info}/WHEEL +0 -0
- {sqlmesh-0.209.1.dev12.dist-info → sqlmesh-0.209.1.dev13.dist-info}/entry_points.txt +0 -0
- {sqlmesh-0.209.1.dev12.dist-info → sqlmesh-0.209.1.dev13.dist-info}/licenses/LICENSE +0 -0
- {sqlmesh-0.209.1.dev12.dist-info → sqlmesh-0.209.1.dev13.dist-info}/top_level.txt +0 -0
sqlmesh/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.209.1.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 209, 1, '
|
|
31
|
+
__version__ = version = '0.209.1.dev13'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 209, 1, 'dev13')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
sqlmesh/core/audit/definition.py
CHANGED
|
@@ -438,13 +438,15 @@ def load_audit(
|
|
|
438
438
|
|
|
439
439
|
extra_kwargs: t.Dict[str, t.Any] = {}
|
|
440
440
|
if is_standalone:
|
|
441
|
-
jinja_macro_refrences,
|
|
441
|
+
jinja_macro_refrences, referenced_variables = extract_macro_references_and_variables(
|
|
442
442
|
*(gen(s) for s in statements),
|
|
443
443
|
gen(query),
|
|
444
444
|
)
|
|
445
445
|
jinja_macros = (jinja_macros or JinjaMacroRegistry()).trim(jinja_macro_refrences)
|
|
446
446
|
for jinja_macro in jinja_macros.root_macros.values():
|
|
447
|
-
|
|
447
|
+
referenced_variables.update(
|
|
448
|
+
extract_macro_references_and_variables(jinja_macro.definition)[1]
|
|
449
|
+
)
|
|
448
450
|
|
|
449
451
|
extra_kwargs["jinja_macros"] = jinja_macros
|
|
450
452
|
extra_kwargs["python_env"] = make_python_env(
|
|
@@ -453,7 +455,7 @@ def load_audit(
|
|
|
453
455
|
module_path,
|
|
454
456
|
macros or macro.get_registry(),
|
|
455
457
|
variables=variables,
|
|
456
|
-
|
|
458
|
+
referenced_variables=referenced_variables,
|
|
457
459
|
)
|
|
458
460
|
extra_kwargs["default_catalog"] = default_catalog
|
|
459
461
|
if project is not None:
|
sqlmesh/core/constants.py
CHANGED
|
@@ -80,7 +80,9 @@ REQUIREMENTS = "sqlmesh-requirements.lock"
|
|
|
80
80
|
DEFAULT_SCHEMA = "default"
|
|
81
81
|
|
|
82
82
|
SQLMESH_VARS = "__sqlmesh__vars__"
|
|
83
|
+
SQLMESH_VARS_METADATA = "__sqlmesh__vars__metadata__"
|
|
83
84
|
SQLMESH_BLUEPRINT_VARS = "__sqlmesh__blueprint__vars__"
|
|
85
|
+
SQLMESH_BLUEPRINT_VARS_METADATA = "__sqlmesh__blueprint__vars__metadata__"
|
|
84
86
|
|
|
85
87
|
VAR = "var"
|
|
86
88
|
BLUEPRINT_VAR = "blueprint_var"
|
sqlmesh/core/macros.py
CHANGED
|
@@ -210,7 +210,12 @@ class MacroEvaluator:
|
|
|
210
210
|
self.macros[normalize_macro_name(k)] = self.env[k]
|
|
211
211
|
elif v.is_value:
|
|
212
212
|
value = self.env[k]
|
|
213
|
-
if k in (
|
|
213
|
+
if k in (
|
|
214
|
+
c.SQLMESH_VARS,
|
|
215
|
+
c.SQLMESH_VARS_METADATA,
|
|
216
|
+
c.SQLMESH_BLUEPRINT_VARS,
|
|
217
|
+
c.SQLMESH_BLUEPRINT_VARS_METADATA,
|
|
218
|
+
):
|
|
214
219
|
value = {
|
|
215
220
|
var_name: (
|
|
216
221
|
self.parse_one(var_value.sql)
|
|
@@ -557,17 +562,25 @@ class MacroEvaluator:
|
|
|
557
562
|
|
|
558
563
|
def var(self, var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.Any]:
|
|
559
564
|
"""Returns the value of the specified variable, or the default value if it doesn't exist."""
|
|
560
|
-
return
|
|
565
|
+
return {
|
|
566
|
+
**(self.locals.get(c.SQLMESH_VARS) or {}),
|
|
567
|
+
**(self.locals.get(c.SQLMESH_VARS_METADATA) or {}),
|
|
568
|
+
}.get(var_name.lower(), default)
|
|
561
569
|
|
|
562
570
|
def blueprint_var(self, var_name: str, default: t.Optional[t.Any] = None) -> t.Optional[t.Any]:
|
|
563
571
|
"""Returns the value of the specified blueprint variable, or the default value if it doesn't exist."""
|
|
564
|
-
return
|
|
572
|
+
return {
|
|
573
|
+
**(self.locals.get(c.SQLMESH_BLUEPRINT_VARS) or {}),
|
|
574
|
+
**(self.locals.get(c.SQLMESH_BLUEPRINT_VARS_METADATA) or {}),
|
|
575
|
+
}.get(var_name.lower(), default)
|
|
565
576
|
|
|
566
577
|
@property
|
|
567
578
|
def variables(self) -> t.Dict[str, t.Any]:
|
|
568
579
|
return {
|
|
569
580
|
**self.locals.get(c.SQLMESH_VARS, {}),
|
|
581
|
+
**self.locals.get(c.SQLMESH_VARS_METADATA, {}),
|
|
570
582
|
**self.locals.get(c.SQLMESH_BLUEPRINT_VARS, {}),
|
|
583
|
+
**self.locals.get(c.SQLMESH_BLUEPRINT_VARS_METADATA, {}),
|
|
571
584
|
}
|
|
572
585
|
|
|
573
586
|
def _coerce(self, expr: exp.Expression, typ: t.Any, strict: bool = False) -> t.Any:
|
sqlmesh/core/model/common.py
CHANGED
|
@@ -28,7 +28,7 @@ if t.TYPE_CHECKING:
|
|
|
28
28
|
from sqlmesh.utils import registry_decorator
|
|
29
29
|
from sqlmesh.utils.jinja import MacroReference
|
|
30
30
|
|
|
31
|
-
MacroCallable = registry_decorator
|
|
31
|
+
MacroCallable = t.Union[Executable, registry_decorator]
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def make_python_env(
|
|
@@ -40,7 +40,7 @@ def make_python_env(
|
|
|
40
40
|
module_path: Path,
|
|
41
41
|
macros: MacroRegistry,
|
|
42
42
|
variables: t.Optional[t.Dict[str, t.Any]] = None,
|
|
43
|
-
|
|
43
|
+
referenced_variables: t.Optional[t.Set[str]] = None,
|
|
44
44
|
path: t.Optional[Path] = None,
|
|
45
45
|
python_env: t.Optional[t.Dict[str, Executable]] = None,
|
|
46
46
|
strict_resolution: bool = True,
|
|
@@ -48,20 +48,64 @@ def make_python_env(
|
|
|
48
48
|
dialect: DialectType = None,
|
|
49
49
|
) -> t.Dict[str, Executable]:
|
|
50
50
|
python_env = {} if python_env is None else python_env
|
|
51
|
-
variables = variables or {}
|
|
52
51
|
env: t.Dict[str, t.Tuple[t.Any, t.Optional[bool]]] = {}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
|
|
53
|
+
variables = variables or {}
|
|
54
|
+
blueprint_variables = blueprint_variables or {}
|
|
55
|
+
|
|
56
|
+
used_macros: t.Dict[str, t.Tuple[MacroCallable, bool]] = {}
|
|
57
|
+
|
|
58
|
+
# var -> True: var is metadata-only
|
|
59
|
+
# var -> False: var is not metadata-only
|
|
60
|
+
# var -> None: cannot determine whether var is metadata-only yet, need to walk macros first
|
|
61
|
+
used_variables: t.Dict[str, t.Optional[bool]] = dict.fromkeys(
|
|
62
|
+
referenced_variables or set(), False
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# id(expr) -> true: expr appears under the AST of a metadata-only macro function
|
|
66
|
+
# id(expr) -> false: expr appears under the AST of a macro function whose metadata status we don't yet know
|
|
67
|
+
expr_under_metadata_macro_func: t.Dict[int, bool] = {}
|
|
68
|
+
|
|
69
|
+
# For @m1(@m2(@x), @y), we'd get x -> m1 and y -> m1
|
|
70
|
+
outermost_macro_func_ancestor_by_var: t.Dict[str, str] = {}
|
|
71
|
+
visited_macro_funcs: t.Set[int] = set()
|
|
72
|
+
|
|
73
|
+
def _is_metadata_var(
|
|
74
|
+
name: str, expression: exp.Expression, appears_in_metadata_expression: bool
|
|
75
|
+
) -> t.Optional[bool]:
|
|
76
|
+
is_metadata_so_far = used_variables.get(name, True)
|
|
77
|
+
if is_metadata_so_far is False:
|
|
78
|
+
# We've concluded this variable is definitely not metadata-only
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
appears_under_metadata_macro_func = expr_under_metadata_macro_func.get(id(expression))
|
|
82
|
+
if is_metadata_so_far and (
|
|
83
|
+
appears_in_metadata_expression or appears_under_metadata_macro_func
|
|
84
|
+
):
|
|
85
|
+
# The variable appears in a metadata expression, e.g., audits (...),
|
|
86
|
+
# or in the AST of metadata-only macro call, e.g., @FOO(@x)
|
|
87
|
+
return True
|
|
88
|
+
|
|
89
|
+
# The variable appears in the AST of a macro call, but we don't know if it's metadata-only
|
|
90
|
+
if appears_under_metadata_macro_func is False:
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
# The variable appears elsewhere, e.g., in the model's query: SELECT @x
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
def _is_metadata_macro(name: str, appears_in_metadata_expression: bool) -> bool:
|
|
97
|
+
if name in used_macros:
|
|
98
|
+
is_metadata_so_far = used_macros[name][1]
|
|
99
|
+
return is_metadata_so_far and appears_in_metadata_expression
|
|
100
|
+
|
|
101
|
+
return appears_in_metadata_expression
|
|
58
102
|
|
|
59
103
|
expressions = ensure_list(expressions)
|
|
60
104
|
for expression_metadata in expressions:
|
|
61
105
|
if isinstance(expression_metadata, tuple):
|
|
62
106
|
expression, is_metadata = expression_metadata
|
|
63
107
|
else:
|
|
64
|
-
expression, is_metadata = expression_metadata,
|
|
108
|
+
expression, is_metadata = expression_metadata, False
|
|
65
109
|
|
|
66
110
|
if isinstance(expression, d.Jinja):
|
|
67
111
|
continue
|
|
@@ -72,31 +116,51 @@ def make_python_env(
|
|
|
72
116
|
if name not in macros:
|
|
73
117
|
continue
|
|
74
118
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
used_macros.get(name, (None, is_metadata))[1] and is_metadata,
|
|
79
|
-
)
|
|
80
|
-
if name == c.VAR:
|
|
119
|
+
used_macros[name] = (macros[name], _is_metadata_macro(name, is_metadata))
|
|
120
|
+
|
|
121
|
+
if name in (c.VAR, c.BLUEPRINT_VAR):
|
|
81
122
|
args = macro_func_or_var.this.expressions
|
|
82
123
|
if len(args) < 1:
|
|
83
|
-
raise_config_error(
|
|
124
|
+
raise_config_error(
|
|
125
|
+
f"Macro {name.upper()} requires at least one argument", path
|
|
126
|
+
)
|
|
127
|
+
|
|
84
128
|
if not args[0].is_string:
|
|
85
129
|
raise_config_error(
|
|
86
130
|
f"The variable name must be a string literal, '{args[0].sql()}' was given instead",
|
|
87
131
|
path,
|
|
88
132
|
)
|
|
89
|
-
|
|
133
|
+
|
|
134
|
+
var_name = args[0].this.lower()
|
|
135
|
+
used_variables[var_name] = _is_metadata_var(
|
|
136
|
+
var_name, macro_func_or_var, is_metadata
|
|
137
|
+
)
|
|
138
|
+
elif id(macro_func_or_var) not in visited_macro_funcs:
|
|
139
|
+
# We only care about the top-level macro function calls to determine the metadata
|
|
140
|
+
# status of the variables referenced in their ASTs. For example, in @m1(@m2(@x)),
|
|
141
|
+
# if m1 is metadata-only but m2 is not, we can still determine that @x only affects
|
|
142
|
+
# the metadata hash, since m2's result feeds into a metadata-only macro function.
|
|
143
|
+
#
|
|
144
|
+
# Generally, if the top-level call is known to be metadata-only or appear in a
|
|
145
|
+
# metadata expression, then we can avoid traversing nested macro function calls.
|
|
146
|
+
|
|
147
|
+
var_refs, _expr_under_metadata_macro_func, _visited_macro_funcs = (
|
|
148
|
+
_extract_macro_func_variable_references(macro_func_or_var, is_metadata)
|
|
149
|
+
)
|
|
150
|
+
expr_under_metadata_macro_func.update(_expr_under_metadata_macro_func)
|
|
151
|
+
visited_macro_funcs.update(_visited_macro_funcs)
|
|
152
|
+
outermost_macro_func_ancestor_by_var |= {var_ref: name for var_ref in var_refs}
|
|
90
153
|
elif macro_func_or_var.__class__ is d.MacroVar:
|
|
91
|
-
|
|
92
|
-
if
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
154
|
+
var_name = macro_func_or_var.name.lower()
|
|
155
|
+
if var_name in macros:
|
|
156
|
+
used_macros[var_name] = (
|
|
157
|
+
macros[var_name],
|
|
158
|
+
_is_metadata_macro(var_name, is_metadata),
|
|
159
|
+
)
|
|
160
|
+
elif var_name in variables or var_name in blueprint_variables:
|
|
161
|
+
used_variables[var_name] = _is_metadata_var(
|
|
162
|
+
var_name, macro_func_or_var, is_metadata
|
|
97
163
|
)
|
|
98
|
-
elif name in variables:
|
|
99
|
-
used_variables.add(name)
|
|
100
164
|
elif (
|
|
101
165
|
isinstance(macro_func_or_var, (exp.Identifier, d.MacroStrReplace, d.MacroSQL))
|
|
102
166
|
) and "@" in macro_func_or_var.name:
|
|
@@ -104,12 +168,14 @@ def make_python_env(
|
|
|
104
168
|
macro_func_or_var.name
|
|
105
169
|
):
|
|
106
170
|
var_name = braced_identifier or identifier
|
|
107
|
-
if var_name in variables:
|
|
108
|
-
used_variables
|
|
171
|
+
if var_name in variables or var_name in blueprint_variables:
|
|
172
|
+
used_variables[var_name] = _is_metadata_var(
|
|
173
|
+
var_name, macro_func_or_var, is_metadata
|
|
174
|
+
)
|
|
109
175
|
|
|
110
176
|
for macro_ref in jinja_macro_references or set():
|
|
111
177
|
if macro_ref.package is None and macro_ref.name in macros:
|
|
112
|
-
used_macros[macro_ref.name] = (macros[macro_ref.name],
|
|
178
|
+
used_macros[macro_ref.name] = (macros[macro_ref.name], False)
|
|
113
179
|
|
|
114
180
|
for name, (used_macro, is_metadata) in used_macros.items():
|
|
115
181
|
if isinstance(used_macro, Executable):
|
|
@@ -131,16 +197,49 @@ def make_python_env(
|
|
|
131
197
|
blueprint_variables=blueprint_variables,
|
|
132
198
|
dialect=dialect,
|
|
133
199
|
strict_resolution=strict_resolution,
|
|
200
|
+
outermost_macro_func_ancestor_by_var=outermost_macro_func_ancestor_by_var,
|
|
134
201
|
)
|
|
135
202
|
|
|
136
203
|
|
|
204
|
+
def _extract_macro_func_variable_references(
|
|
205
|
+
macro_func: exp.Expression,
|
|
206
|
+
is_metadata: bool,
|
|
207
|
+
) -> t.Tuple[t.Set[str], t.Dict[int, bool], t.Set[int]]:
|
|
208
|
+
var_references = set()
|
|
209
|
+
visited_macro_funcs = set()
|
|
210
|
+
expr_under_metadata_macro_func = {}
|
|
211
|
+
|
|
212
|
+
for n in macro_func.walk():
|
|
213
|
+
if type(n) is d.MacroFunc:
|
|
214
|
+
visited_macro_funcs.add(id(n))
|
|
215
|
+
|
|
216
|
+
this = n.this
|
|
217
|
+
args = this.expressions
|
|
218
|
+
|
|
219
|
+
if this.name.lower() in (c.VAR, c.BLUEPRINT_VAR) and args and args[0].is_string:
|
|
220
|
+
var_references.add(args[0].this.lower())
|
|
221
|
+
expr_under_metadata_macro_func[id(n)] = is_metadata
|
|
222
|
+
elif isinstance(n, d.MacroVar):
|
|
223
|
+
var_references.add(n.name.lower())
|
|
224
|
+
expr_under_metadata_macro_func[id(n)] = is_metadata
|
|
225
|
+
elif isinstance(n, (exp.Identifier, d.MacroStrReplace, d.MacroSQL)) and "@" in n.name:
|
|
226
|
+
var_references.update(
|
|
227
|
+
(braced_identifier or identifier).lower()
|
|
228
|
+
for _, identifier, braced_identifier, _ in MacroStrTemplate.pattern.findall(n.name)
|
|
229
|
+
)
|
|
230
|
+
expr_under_metadata_macro_func[id(n)] = is_metadata
|
|
231
|
+
|
|
232
|
+
return (var_references, expr_under_metadata_macro_func, visited_macro_funcs)
|
|
233
|
+
|
|
234
|
+
|
|
137
235
|
def _add_variables_to_python_env(
|
|
138
236
|
python_env: t.Dict[str, Executable],
|
|
139
|
-
used_variables: t.
|
|
237
|
+
used_variables: t.Dict[str, t.Optional[bool]],
|
|
140
238
|
variables: t.Optional[t.Dict[str, t.Any]],
|
|
141
239
|
strict_resolution: bool = True,
|
|
142
240
|
blueprint_variables: t.Optional[t.Dict[str, t.Any]] = None,
|
|
143
241
|
dialect: DialectType = None,
|
|
242
|
+
outermost_macro_func_ancestor_by_var: t.Optional[t.Dict[str, str]] = None,
|
|
144
243
|
) -> t.Dict[str, Executable]:
|
|
145
244
|
_, python_used_variables = parse_dependencies(
|
|
146
245
|
python_env,
|
|
@@ -149,20 +248,67 @@ def _add_variables_to_python_env(
|
|
|
149
248
|
variables=variables,
|
|
150
249
|
blueprint_variables=blueprint_variables,
|
|
151
250
|
)
|
|
152
|
-
|
|
251
|
+
for var_name, is_metadata in python_used_variables.items():
|
|
252
|
+
used_variables[var_name] = is_metadata and used_variables.get(var_name, True)
|
|
253
|
+
|
|
254
|
+
# Variables are treated as metadata-only when all of their references either:
|
|
255
|
+
# - appear in metadata-only expressions, such as `audits (...)`, virtual statements, etc
|
|
256
|
+
# - appear in the ASTs or definitions of metadata-only macros
|
|
257
|
+
#
|
|
258
|
+
# See also: https://github.com/TobikoData/sqlmesh/pull/4936#issuecomment-3136339936,
|
|
259
|
+
# specifically the "Terminology" and "Observations" section.
|
|
260
|
+
metadata_used_variables = {
|
|
261
|
+
var_name for var_name, is_metadata in used_variables.items() if is_metadata
|
|
262
|
+
}
|
|
263
|
+
for used_var, outermost_macro_func in (outermost_macro_func_ancestor_by_var or {}).items():
|
|
264
|
+
used_var_is_metadata = used_variables.get(used_var)
|
|
265
|
+
if used_var_is_metadata is False:
|
|
266
|
+
continue
|
|
267
|
+
|
|
268
|
+
# At this point we can decide whether a variable reference in a macro call's AST is
|
|
269
|
+
# metadata-only, because we've annotated the corresponding macro call in the python env.
|
|
270
|
+
if outermost_macro_func in python_env and python_env[outermost_macro_func].is_metadata:
|
|
271
|
+
metadata_used_variables.add(used_var)
|
|
272
|
+
|
|
273
|
+
non_metadata_used_variables = set(used_variables) - metadata_used_variables
|
|
274
|
+
|
|
275
|
+
if overlapping_variables := (non_metadata_used_variables & metadata_used_variables):
|
|
276
|
+
raise ConfigError(
|
|
277
|
+
f"Variables {', '.join(overlapping_variables)} are both metadata and non-metadata, "
|
|
278
|
+
"which is unexpected. Please file an issue at https://github.com/TobikoData/sqlmesh/issues/new."
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
metadata_variables = {
|
|
282
|
+
k: v for k, v in (variables or {}).items() if k in metadata_used_variables
|
|
283
|
+
}
|
|
284
|
+
variables = {k: v for k, v in (variables or {}).items() if k in non_metadata_used_variables}
|
|
153
285
|
|
|
154
|
-
variables = {k: v for k, v in (variables or {}).items() if k in used_variables}
|
|
155
286
|
if variables:
|
|
156
287
|
python_env[c.SQLMESH_VARS] = Executable.value(variables, sort_root_dict=True)
|
|
288
|
+
if metadata_variables:
|
|
289
|
+
python_env[c.SQLMESH_VARS_METADATA] = Executable.value(
|
|
290
|
+
metadata_variables, sort_root_dict=True, is_metadata=True
|
|
291
|
+
)
|
|
157
292
|
|
|
158
293
|
if blueprint_variables:
|
|
294
|
+
metadata_blueprint_variables = {
|
|
295
|
+
k: SqlValue(sql=v.sql(dialect=dialect)) if isinstance(v, exp.Expression) else v
|
|
296
|
+
for k, v in blueprint_variables.items()
|
|
297
|
+
if k in metadata_used_variables
|
|
298
|
+
}
|
|
159
299
|
blueprint_variables = {
|
|
160
300
|
k.lower(): SqlValue(sql=v.sql(dialect=dialect)) if isinstance(v, exp.Expression) else v
|
|
161
301
|
for k, v in blueprint_variables.items()
|
|
302
|
+
if k in non_metadata_used_variables
|
|
162
303
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
304
|
+
if blueprint_variables:
|
|
305
|
+
python_env[c.SQLMESH_BLUEPRINT_VARS] = Executable.value(
|
|
306
|
+
blueprint_variables, sort_root_dict=True
|
|
307
|
+
)
|
|
308
|
+
if metadata_blueprint_variables:
|
|
309
|
+
python_env[c.SQLMESH_BLUEPRINT_VARS_METADATA] = Executable.value(
|
|
310
|
+
metadata_blueprint_variables, sort_root_dict=True, is_metadata=True
|
|
311
|
+
)
|
|
166
312
|
|
|
167
313
|
return python_env
|
|
168
314
|
|
|
@@ -173,7 +319,7 @@ def parse_dependencies(
|
|
|
173
319
|
strict_resolution: bool = True,
|
|
174
320
|
variables: t.Optional[t.Dict[str, t.Any]] = None,
|
|
175
321
|
blueprint_variables: t.Optional[t.Dict[str, t.Any]] = None,
|
|
176
|
-
) -> t.Tuple[t.Set[str], t.
|
|
322
|
+
) -> t.Tuple[t.Set[str], t.Dict[str, bool]]:
|
|
177
323
|
"""
|
|
178
324
|
Parses the source of a model function and finds upstream table dependencies
|
|
179
325
|
and referenced variables based on calls to context / evaluator.
|
|
@@ -187,7 +333,8 @@ def parse_dependencies(
|
|
|
187
333
|
blueprint_variables: The blueprint variables available to the python environment.
|
|
188
334
|
|
|
189
335
|
Returns:
|
|
190
|
-
A tuple containing the set of upstream table dependencies and
|
|
336
|
+
A tuple containing the set of upstream table dependencies and a mapping of
|
|
337
|
+
the referenced variables associated with their metadata status.
|
|
191
338
|
"""
|
|
192
339
|
|
|
193
340
|
class VariableResolutionContext:
|
|
@@ -205,12 +352,16 @@ def parse_dependencies(
|
|
|
205
352
|
local_env = dict.fromkeys(("context", "evaluator"), VariableResolutionContext)
|
|
206
353
|
|
|
207
354
|
depends_on = set()
|
|
208
|
-
used_variables =
|
|
355
|
+
used_variables: t.Dict[str, bool] = {}
|
|
209
356
|
|
|
210
357
|
for executable in python_env.values():
|
|
211
358
|
if not executable.is_definition:
|
|
212
359
|
continue
|
|
360
|
+
|
|
361
|
+
is_metadata = executable.is_metadata
|
|
213
362
|
for node in ast.walk(ast.parse(executable.payload)):
|
|
363
|
+
next_variables = set()
|
|
364
|
+
|
|
214
365
|
if isinstance(node, ast.Call):
|
|
215
366
|
func = node.func
|
|
216
367
|
if not isinstance(func, ast.Attribute) or not isinstance(func.value, ast.Name):
|
|
@@ -241,8 +392,11 @@ def parse_dependencies(
|
|
|
241
392
|
|
|
242
393
|
if func.value.id == "context" and func.attr in ("table", "resolve_table"):
|
|
243
394
|
depends_on.add(get_first_arg("model_name"))
|
|
244
|
-
elif func.value.id in ("context", "evaluator") and func.attr
|
|
245
|
-
|
|
395
|
+
elif func.value.id in ("context", "evaluator") and func.attr in (
|
|
396
|
+
c.VAR,
|
|
397
|
+
c.BLUEPRINT_VAR,
|
|
398
|
+
):
|
|
399
|
+
next_variables.add(get_first_arg("var_name").lower())
|
|
246
400
|
elif (
|
|
247
401
|
isinstance(node, ast.Attribute)
|
|
248
402
|
and isinstance(node.value, ast.Name)
|
|
@@ -250,9 +404,9 @@ def parse_dependencies(
|
|
|
250
404
|
and node.attr == c.GATEWAY
|
|
251
405
|
):
|
|
252
406
|
# Check whether the gateway attribute is referenced.
|
|
253
|
-
|
|
407
|
+
next_variables.add(c.GATEWAY)
|
|
254
408
|
elif isinstance(node, ast.FunctionDef) and node.name == entrypoint:
|
|
255
|
-
|
|
409
|
+
next_variables.update(
|
|
256
410
|
[
|
|
257
411
|
arg.arg
|
|
258
412
|
for arg in [*node.args.args, *node.args.kwonlyargs]
|
|
@@ -260,6 +414,9 @@ def parse_dependencies(
|
|
|
260
414
|
]
|
|
261
415
|
)
|
|
262
416
|
|
|
417
|
+
for var_name in next_variables:
|
|
418
|
+
used_variables[var_name] = used_variables.get(var_name, True) and bool(is_metadata)
|
|
419
|
+
|
|
263
420
|
return depends_on, used_variables
|
|
264
421
|
|
|
265
422
|
|
sqlmesh/core/model/definition.py
CHANGED
|
@@ -1782,12 +1782,17 @@ class PythonModel(_Model):
|
|
|
1782
1782
|
start, end = make_inclusive(start or c.EPOCH, end or c.EPOCH, self.dialect)
|
|
1783
1783
|
execution_time = to_datetime(execution_time or c.EPOCH)
|
|
1784
1784
|
|
|
1785
|
-
variables =
|
|
1786
|
-
|
|
1787
|
-
|
|
1785
|
+
variables = {
|
|
1786
|
+
**env.get(c.SQLMESH_VARS, {}),
|
|
1787
|
+
**env.get(c.SQLMESH_VARS_METADATA, {}),
|
|
1788
|
+
**kwargs.pop("variables", {}),
|
|
1789
|
+
}
|
|
1788
1790
|
blueprint_variables = {
|
|
1789
1791
|
k: d.parse_one(v.sql, dialect=self.dialect) if isinstance(v, SqlValue) else v
|
|
1790
|
-
for k, v in
|
|
1792
|
+
for k, v in {
|
|
1793
|
+
**env.get(c.SQLMESH_BLUEPRINT_VARS, {}),
|
|
1794
|
+
**env.get(c.SQLMESH_BLUEPRINT_VARS_METADATA, {}),
|
|
1795
|
+
}.items()
|
|
1791
1796
|
}
|
|
1792
1797
|
try:
|
|
1793
1798
|
kwargs = {
|
|
@@ -1909,11 +1914,11 @@ def _extract_blueprint_variables(blueprint: t.Any, path: Path) -> t.Dict[str, t.
|
|
|
1909
1914
|
return {}
|
|
1910
1915
|
if isinstance(blueprint, (exp.Paren, exp.PropertyEQ)):
|
|
1911
1916
|
blueprint = blueprint.unnest()
|
|
1912
|
-
return {blueprint.left.name: blueprint.right}
|
|
1917
|
+
return {blueprint.left.name.lower(): blueprint.right}
|
|
1913
1918
|
if isinstance(blueprint, (exp.Tuple, exp.Array)):
|
|
1914
|
-
return {e.left.name: e.right for e in blueprint.expressions}
|
|
1919
|
+
return {e.left.name.lower(): e.right for e in blueprint.expressions}
|
|
1915
1920
|
if isinstance(blueprint, dict):
|
|
1916
|
-
return blueprint
|
|
1921
|
+
return {k.lower(): v for k, v in blueprint.items()}
|
|
1917
1922
|
|
|
1918
1923
|
raise_config_error(
|
|
1919
1924
|
f"Expected a key-value mapping for the blueprint value, got '{blueprint}' instead",
|
|
@@ -2509,7 +2514,7 @@ def _create_model(
|
|
|
2509
2514
|
if isinstance(getattr(kwargs.get("kind"), "merge_filter", None), exp.Expression):
|
|
2510
2515
|
statements.append(kwargs["kind"].merge_filter)
|
|
2511
2516
|
|
|
2512
|
-
jinja_macro_references,
|
|
2517
|
+
jinja_macro_references, referenced_variables = extract_macro_references_and_variables(
|
|
2513
2518
|
*(gen(e if isinstance(e, exp.Expression) else e[0]) for e in statements)
|
|
2514
2519
|
)
|
|
2515
2520
|
|
|
@@ -2532,11 +2537,13 @@ def _create_model(
|
|
|
2532
2537
|
_extract_migrated_dbt_variable_references(jinja_macros, variables)
|
|
2533
2538
|
)
|
|
2534
2539
|
|
|
2535
|
-
|
|
2540
|
+
referenced_variables.update(nested_macro_used_variables)
|
|
2536
2541
|
variables.update(flattened_package_variables)
|
|
2537
2542
|
else:
|
|
2538
2543
|
for jinja_macro in jinja_macros.root_macros.values():
|
|
2539
|
-
|
|
2544
|
+
referenced_variables.update(
|
|
2545
|
+
extract_macro_references_and_variables(jinja_macro.definition)[1]
|
|
2546
|
+
)
|
|
2540
2547
|
|
|
2541
2548
|
# Merge model-specific audits with default audits
|
|
2542
2549
|
if default_audits := defaults.pop("audits", None):
|
|
@@ -2598,7 +2605,7 @@ def _create_model(
|
|
|
2598
2605
|
module_path,
|
|
2599
2606
|
macros or macro.get_registry(),
|
|
2600
2607
|
variables=variables,
|
|
2601
|
-
|
|
2608
|
+
referenced_variables=referenced_variables,
|
|
2602
2609
|
path=path,
|
|
2603
2610
|
python_env=python_env,
|
|
2604
2611
|
strict_resolution=depends_on is None,
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This script's goal is to warn users about two situations that could lead to a diff:
|
|
3
|
+
|
|
4
|
+
- They have blueprint models and some of their variables may be trimmed from `python_env`
|
|
5
|
+
- Variables are used in metadata-only contexts, e.g., within metadata-only macros
|
|
6
|
+
|
|
7
|
+
Context:
|
|
8
|
+
|
|
9
|
+
We used to store *all* blueprint variables in `python_env`, even though some of them were
|
|
10
|
+
redundant. For example, if a blueprint variable is only used in the model's `name` property,
|
|
11
|
+
then it is rendered once, at load time, and after that point it's not needed elsewhere.
|
|
12
|
+
|
|
13
|
+
This behavior is now different: we only store the blueprint variables that are required to render
|
|
14
|
+
expressions at runtime, such as model query or runtime-rendered properties, like `merge_filter`.
|
|
15
|
+
|
|
16
|
+
Additionally, variables were previously treated as non-metadata, regardless of how they were used.
|
|
17
|
+
This behavior changed as well: SQLMesh now analyzes variable references and tracks the data flow,
|
|
18
|
+
in order to detect whether changing them will result in a metadata diff for a given model.
|
|
19
|
+
|
|
20
|
+
Some examples where variables can be treated as metadata-only `python_env` executables are:
|
|
21
|
+
|
|
22
|
+
- A variable is referenced in metadata-only macros
|
|
23
|
+
- A variable is referenced in metadata-only expressions, such as virtual update statements
|
|
24
|
+
- A variable is passed as argument to metadata-only macros
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import json
|
|
28
|
+
|
|
29
|
+
from sqlglot import exp
|
|
30
|
+
|
|
31
|
+
from sqlmesh.core.console import get_console
|
|
32
|
+
|
|
33
|
+
SQLMESH_VARS = "__sqlmesh__vars__"
|
|
34
|
+
SQLMESH_BLUEPRINT_VARS = "__sqlmesh__blueprint__vars__"
|
|
35
|
+
METADATA_HASH_EXPRESSIONS = {"on_virtual_update", "audits", "signals", "audit_definitions"}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def migrate(state_sync, **kwargs): # type: ignore
|
|
39
|
+
engine_adapter = state_sync.engine_adapter
|
|
40
|
+
schema = state_sync.schema
|
|
41
|
+
snapshots_table = "_snapshots"
|
|
42
|
+
if schema:
|
|
43
|
+
snapshots_table = f"{schema}.{snapshots_table}"
|
|
44
|
+
|
|
45
|
+
warning = (
|
|
46
|
+
"SQLMesh detected that it may not be able to fully migrate the state database. This should not impact "
|
|
47
|
+
"the migration process, but may result in unexpected changes being reported by the next `sqlmesh plan` "
|
|
48
|
+
"command. Please run `sqlmesh diff prod` after the migration has completed, before making any new "
|
|
49
|
+
"changes. If any unexpected changes are reported, consider running a forward-only plan to apply these "
|
|
50
|
+
"changes and avoid unnecessary backfills: sqlmesh plan prod --forward-only. "
|
|
51
|
+
"See https://sqlmesh.readthedocs.io/en/stable/concepts/plans/#forward-only-plans for more details.\n"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
for (snapshot,) in engine_adapter.fetchall(
|
|
55
|
+
exp.select("snapshot").from_(snapshots_table), quote_identifiers=True
|
|
56
|
+
):
|
|
57
|
+
parsed_snapshot = json.loads(snapshot)
|
|
58
|
+
node = parsed_snapshot["node"]
|
|
59
|
+
|
|
60
|
+
# Standalone audits don't have a data hash, so they're unaffected
|
|
61
|
+
if node.get("source_type") == "audit":
|
|
62
|
+
continue
|
|
63
|
+
|
|
64
|
+
python_env = node.get("python_env") or {}
|
|
65
|
+
|
|
66
|
+
if SQLMESH_BLUEPRINT_VARS in python_env or (
|
|
67
|
+
SQLMESH_VARS in python_env
|
|
68
|
+
and (
|
|
69
|
+
any(v.get("is_metadata") for v in python_env.values())
|
|
70
|
+
or any(node.get(k) for k in METADATA_HASH_EXPRESSIONS)
|
|
71
|
+
)
|
|
72
|
+
):
|
|
73
|
+
get_console().log_warning(warning)
|
|
74
|
+
return
|
sqlmesh/utils/jinja.py
CHANGED
|
@@ -229,7 +229,7 @@ def extract_macro_references_and_variables(
|
|
|
229
229
|
)
|
|
230
230
|
|
|
231
231
|
for call_name, node in extract_call_names(jinja_str):
|
|
232
|
-
if call_name[0]
|
|
232
|
+
if call_name[0] in (c.VAR, c.BLUEPRINT_VAR):
|
|
233
233
|
assert isinstance(node, nodes.Call)
|
|
234
234
|
args = [jinja_call_arg_name(arg) for arg in node.args]
|
|
235
235
|
if args and args[0]:
|
sqlmesh/utils/metaprogramming.py
CHANGED
|
@@ -283,7 +283,7 @@ def build_env(
|
|
|
283
283
|
env: t.Dict[str, t.Tuple[t.Any, t.Optional[bool]]],
|
|
284
284
|
name: str,
|
|
285
285
|
path: Path,
|
|
286
|
-
is_metadata_obj:
|
|
286
|
+
is_metadata_obj: bool = False,
|
|
287
287
|
) -> None:
|
|
288
288
|
"""Fills in env dictionary with all globals needed to execute the object.
|
|
289
289
|
|
|
@@ -299,7 +299,7 @@ def build_env(
|
|
|
299
299
|
# We don't rely on `env` to keep track of visited objects, because it's populated in post-order
|
|
300
300
|
visited: t.Set[str] = set()
|
|
301
301
|
|
|
302
|
-
def walk(obj: t.Any, name: str, is_metadata:
|
|
302
|
+
def walk(obj: t.Any, name: str, is_metadata: bool = False) -> None:
|
|
303
303
|
obj_module = inspect.getmodule(obj)
|
|
304
304
|
if obj_module and obj_module.__name__ == "builtins":
|
|
305
305
|
return
|
|
@@ -320,7 +320,7 @@ def build_env(
|
|
|
320
320
|
# The existing object in the env is "metadata only" but we're walking it again as a
|
|
321
321
|
# non-"metadata only" dependency, so we update this flag to ensure all transitive
|
|
322
322
|
# dependencies are also not marked as "metadata only"
|
|
323
|
-
is_metadata =
|
|
323
|
+
is_metadata = False
|
|
324
324
|
|
|
325
325
|
if hasattr(obj, c.SQLMESH_MACRO):
|
|
326
326
|
# We only need to add the undecorated code of @macro() functions in env, which
|
|
@@ -380,7 +380,7 @@ def build_env(
|
|
|
380
380
|
)
|
|
381
381
|
|
|
382
382
|
# The "metadata only" annotation of the object is transitive
|
|
383
|
-
walk(obj, name, is_metadata_obj or getattr(obj, c.SQLMESH_METADATA,
|
|
383
|
+
walk(obj, name, is_metadata_obj or getattr(obj, c.SQLMESH_METADATA, False))
|
|
384
384
|
|
|
385
385
|
|
|
386
386
|
@dataclass
|
|
@@ -432,7 +432,11 @@ class Executable(PydanticModel):
|
|
|
432
432
|
cls, v: t.Any, is_metadata: t.Optional[bool] = None, sort_root_dict: bool = False
|
|
433
433
|
) -> Executable:
|
|
434
434
|
payload = _dict_sort(v) if sort_root_dict else repr(v)
|
|
435
|
-
return Executable(
|
|
435
|
+
return Executable(
|
|
436
|
+
payload=payload,
|
|
437
|
+
kind=ExecutableKind.VALUE,
|
|
438
|
+
is_metadata=is_metadata or None,
|
|
439
|
+
)
|
|
436
440
|
|
|
437
441
|
|
|
438
442
|
def serialize_env(env: t.Dict[str, t.Any], path: Path) -> t.Dict[str, Executable]:
|
|
@@ -447,6 +451,9 @@ def serialize_env(env: t.Dict[str, t.Any], path: Path) -> t.Dict[str, Executable
|
|
|
447
451
|
serialized = {}
|
|
448
452
|
|
|
449
453
|
for k, (v, is_metadata) in env.items():
|
|
454
|
+
# We don't store `False` for `is_metadata` to reduce the pydantic model's payload size
|
|
455
|
+
is_metadata = is_metadata or None
|
|
456
|
+
|
|
450
457
|
if isinstance(v, LITERALS) or v is None:
|
|
451
458
|
serialized[k] = Executable.value(v, is_metadata=is_metadata)
|
|
452
459
|
elif inspect.ismodule(v):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
sqlmesh/__init__.py,sha256=QjwaamJIdW7voXoS880yAx7tHm-Im4_mqGA-XJgafos,7631
|
|
2
|
-
sqlmesh/_version.py,sha256=
|
|
2
|
+
sqlmesh/_version.py,sha256=Z-QEmLpn7WCZTovKI1S7q9PMPW5I4VTOb_KNgW5Qf5Y,723
|
|
3
3
|
sqlmesh/magics.py,sha256=bEzYqXHcM0P3CjyUZ1O9arlmXAdLAhoglKbQ29fD9AE,41842
|
|
4
4
|
sqlmesh/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
sqlmesh/cicd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -12,14 +12,14 @@ sqlmesh/cli/project_init.py,sha256=5JmDmUK0WJs39hM-p76uzHYdo1Hnx6fKuJg_pR3Hn5A,1
|
|
|
12
12
|
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=KbIXoxt5hpDENEolY-DpBlPmcWtJ9vzEgF52mjG2jPE,165710
|
|
15
|
-
sqlmesh/core/constants.py,sha256=
|
|
15
|
+
sqlmesh/core/constants.py,sha256=Wg6qApCRhENA1me7ELUS5GnArKlTBH-8192gQYKQnE8,2759
|
|
16
16
|
sqlmesh/core/context.py,sha256=bFAvnjg3YENIgWVDYfflKA5yW6o6gMEZhiSKBPIrBk8,127613
|
|
17
17
|
sqlmesh/core/context_diff.py,sha256=_Ur55fGLJ58Icsp44I4pVhdi7_JHiHq45mWIco9oFzk,21792
|
|
18
18
|
sqlmesh/core/dialect.py,sha256=wJ2cDoxz72LIRJ3iBZdCqtnKMbMUdIftl-AvbVldI28,52413
|
|
19
19
|
sqlmesh/core/environment.py,sha256=odus40R-YzdQdS_IiUq_tFouUXIhnpwmNwo8dE8Aw1k,13041
|
|
20
20
|
sqlmesh/core/lineage.py,sha256=zjB0Zfamo2Fja2r5SSZPMbrEKTXr1WjozZPVqvxdybI,3143
|
|
21
21
|
sqlmesh/core/loader.py,sha256=z8KNT5igXc_g-5X1BPPL9XAbpve3ZQp7OkkX63iT0Gc,41179
|
|
22
|
-
sqlmesh/core/macros.py,sha256=
|
|
22
|
+
sqlmesh/core/macros.py,sha256=G1TD1Lv-iTNOh1JtHwRFOrnThqDBFuAQgQJtp6UDe0I,63023
|
|
23
23
|
sqlmesh/core/node.py,sha256=3ChBz1PIMJhp-jzed_GLBrtmEF2v_BVDEM5WNe0ssPk,14658
|
|
24
24
|
sqlmesh/core/notification_target.py,sha256=PPGoDrgbRKxr27vJEu03XqNTQLYTw0ZF_b0yAapxGeI,16158
|
|
25
25
|
sqlmesh/core/reference.py,sha256=k7OSkLqTjPR8WJjNeFj0xAJ297nZUMgb_iTVwKRRKjc,4875
|
|
@@ -36,7 +36,7 @@ sqlmesh/core/analytics/collector.py,sha256=sggJvER16Jrg5triTC27E2KwaQvfkVDv4D6R1
|
|
|
36
36
|
sqlmesh/core/analytics/dispatcher.py,sha256=SQw5tHIETb7ei_oCj7ktHEJyeJNbKyIYiOPxQXyQuIg,6259
|
|
37
37
|
sqlmesh/core/audit/__init__.py,sha256=3jogKe-G4X0rOxwH5QVuRRioyesQffSWxKU4PVwMPvA,216
|
|
38
38
|
sqlmesh/core/audit/builtin.py,sha256=adA-cFCphB66jVY4n66cbwbRTFF1FTEIMrLhRDpbBo0,17783
|
|
39
|
-
sqlmesh/core/audit/definition.py,sha256=
|
|
39
|
+
sqlmesh/core/audit/definition.py,sha256=iPpGEOwFIXFHQFQGMyapCm7jnlotwMa-gNaCgTwzlIA,18360
|
|
40
40
|
sqlmesh/core/config/__init__.py,sha256=5zxTXjUMuxBj4XlX6bmpIWTp4GsZY5rIdKSCTYbxRBM,2070
|
|
41
41
|
sqlmesh/core/config/base.py,sha256=t8NQmsgQoZSc-k0dlDiCb8t1jj0AMYdGZ-6se9q_Pks,4898
|
|
42
42
|
sqlmesh/core/config/categorizer.py,sha256=6vzUoNLjR6GOEb_2mYVz2TwmMv2BfldgHX2u-Le5HZs,1975
|
|
@@ -86,9 +86,9 @@ sqlmesh/core/metric/definition.py,sha256=Yd5aVgsZCDPJ43aGP7WqtzZOuuSUtB8uJGVA6Jw
|
|
|
86
86
|
sqlmesh/core/metric/rewriter.py,sha256=GiSTHfn2kinqCfNPYgZPRk93JFLzVaaejHtHDQ0yXZI,7326
|
|
87
87
|
sqlmesh/core/model/__init__.py,sha256=C8GRZ53xuXEA9hQv3BQS9pNNyd9rZ06R_B96UYGhDu4,1644
|
|
88
88
|
sqlmesh/core/model/cache.py,sha256=csun0RJguHzKX6-qITcOs4fVP4f8_Ts8qiUVV4sHY6Q,7869
|
|
89
|
-
sqlmesh/core/model/common.py,sha256=
|
|
89
|
+
sqlmesh/core/model/common.py,sha256=xCMdwkJXnwjE9-56wNb1HiER0KHmWbBzc2Tm--2u168,25610
|
|
90
90
|
sqlmesh/core/model/decorator.py,sha256=wUUg5t0K559tV9Pqt6FE2EWXt5TPGb4wF2Yg4B4hX4w,9251
|
|
91
|
-
sqlmesh/core/model/definition.py,sha256=
|
|
91
|
+
sqlmesh/core/model/definition.py,sha256=uouwIbWA-m2cFXxvDrBVHKxR0ycSAq8Rz3Qv3xY7ewA,113852
|
|
92
92
|
sqlmesh/core/model/kind.py,sha256=uN73ycANfM94CxcOdkUTclLansNvwo3wySkkl--QzY0,36322
|
|
93
93
|
sqlmesh/core/model/meta.py,sha256=IE-b_OkCcKzXuQBsopiaf0l5a5Ay903N_64Z1s4cXGk,20064
|
|
94
94
|
sqlmesh/core/model/schema.py,sha256=_HMYfzK9wWXh7_CQDIIGnuQUD4aiX3o5D2cRp2sERzc,3387
|
|
@@ -264,6 +264,7 @@ sqlmesh/migrations/v0084_normalize_quote_when_matched_and_merge_filter.py,sha256
|
|
|
264
264
|
sqlmesh/migrations/v0085_deterministic_repr.py,sha256=mkbZV0rzXJ5bmN1Y75kFI9T6gbnx_sy-zn_UzbOL1Sg,4265
|
|
265
265
|
sqlmesh/migrations/v0086_check_deterministic_bug.py,sha256=WiYcmhQD434GXWiiakQtcy9ESmB35spXMltV43nvU7Q,2953
|
|
266
266
|
sqlmesh/migrations/v0087_normalize_blueprint_variables.py,sha256=GI6KKA-6PtONUoOnyvLpRTUmPjbpa-CKhDa0iqJW01U,4421
|
|
267
|
+
sqlmesh/migrations/v0088_warn_about_variable_python_env_diffs.py,sha256=zCLT6Yqwny73SlfACWjO1bZfs8lZ7gJBov5Iz4akwiQ,3254
|
|
267
268
|
sqlmesh/schedulers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
268
269
|
sqlmesh/utils/__init__.py,sha256=51VqA2etTZ6kX54250s1hN5ui7D89OeUTUQ9kgMze7s,11554
|
|
269
270
|
sqlmesh/utils/aws.py,sha256=krxyhrKtsMJDEjT567UbGZogjJ9JRHkvAV-Cg3FVndM,1062
|
|
@@ -278,9 +279,9 @@ sqlmesh/utils/date.py,sha256=uS0Kii4hFJeTXqXtM39sDCzvFhTAoPk_NPg51T1YYeo,16218
|
|
|
278
279
|
sqlmesh/utils/errors.py,sha256=8FE33Eo3KN3Y4s5EZM5t92jZi9BAVGzkr3QCGlrLeDk,5679
|
|
279
280
|
sqlmesh/utils/git.py,sha256=v1MD4Zwn52UBn_tTfoKn8SAPJVGC6SEFrJ4WEJFbgF0,1868
|
|
280
281
|
sqlmesh/utils/hashing.py,sha256=nZRKvLNQ83tLG4IoXshVJZf-MbDrXC1HOeNw8Ji-tMM,578
|
|
281
|
-
sqlmesh/utils/jinja.py,sha256=
|
|
282
|
+
sqlmesh/utils/jinja.py,sha256=7IPOu4pjUmxX_ZNL7S0UN23CXJM8EUzPaFt3Wzr1rxI,27863
|
|
282
283
|
sqlmesh/utils/lineage.py,sha256=zz9BPc6MShRy9LEXmAp02x6oKt4ubVNUPdapFVFKkac,16019
|
|
283
|
-
sqlmesh/utils/metaprogramming.py,sha256=
|
|
284
|
+
sqlmesh/utils/metaprogramming.py,sha256=6NQKi9PpYhgJEu0Wr_TztRZZ2Kog4Dic9syNqbvztMw,23727
|
|
284
285
|
sqlmesh/utils/migration.py,sha256=R7Ck6FX-LfcujGy7sgS0kDWlK-xAEBODr3gqIQ8gC08,835
|
|
285
286
|
sqlmesh/utils/pandas.py,sha256=FytTSLcRNtO0-YMvpoDBkkut3yoQJR9mBSKpif8lxug,2497
|
|
286
287
|
sqlmesh/utils/process.py,sha256=vpXcpJ1eIbivs7gZdwTKSJI7OS6kKBebnaBzz75ykvs,2428
|
|
@@ -288,7 +289,7 @@ sqlmesh/utils/pydantic.py,sha256=o_NsXbIpDqNpUA1Uc5xF0ZzoXQYYB0DfHwdRxBwXPNk,120
|
|
|
288
289
|
sqlmesh/utils/rich.py,sha256=cwQ5nJ6sgz64xHtoh6_ec7ReV5YpsOGhMtUJnwoRfEI,3549
|
|
289
290
|
sqlmesh/utils/windows.py,sha256=r7Yin4WEwbvH7sKA3hOJtRsclmscNuBeCklCfVPvtQs,611
|
|
290
291
|
sqlmesh/utils/yaml.py,sha256=hvo5lmybYLcIx4p6CQwufS8X4MbwS60lEBnCmYJCXmU,2336
|
|
291
|
-
sqlmesh-0.209.1.
|
|
292
|
+
sqlmesh-0.209.1.dev13.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
|
|
292
293
|
sqlmesh_dbt/__init__.py,sha256=awYS5y5mz-1NUmx6i5h5NSTJ7tidRl9NC0FAnFWSF6U,350
|
|
293
294
|
sqlmesh_dbt/cli.py,sha256=Ekn77WLMmyyr31aoDoWrZp5DS0rfiddy7pNohBK8G0s,2115
|
|
294
295
|
sqlmesh_dbt/console.py,sha256=1B0B5_R1DlrEkyWVDl8G78-B6_K-ZtLPsQEhqPCfLEU,188
|
|
@@ -410,8 +411,8 @@ web/server/api/endpoints/models.py,sha256=kwj0s7uve3iZSMfmjkoPVMFMeY1sD0peTeyrWf
|
|
|
410
411
|
web/server/api/endpoints/modules.py,sha256=8hqqgonGay_mJmpCw0IdbjsPhWlQH2VLdKAqha-myac,468
|
|
411
412
|
web/server/api/endpoints/plan.py,sha256=bbbY50W_2MsZSTxOHWMKz0tbIm75nsRSlPy8GI2fg9Q,9306
|
|
412
413
|
web/server/api/endpoints/table_diff.py,sha256=SCy3-pr5x6bEEiOaZGCDbiHQhpgxwmnz3Acqa2Ie6jI,2090
|
|
413
|
-
sqlmesh-0.209.1.
|
|
414
|
-
sqlmesh-0.209.1.
|
|
415
|
-
sqlmesh-0.209.1.
|
|
416
|
-
sqlmesh-0.209.1.
|
|
417
|
-
sqlmesh-0.209.1.
|
|
414
|
+
sqlmesh-0.209.1.dev13.dist-info/METADATA,sha256=xyA8vDp0_wasj_KFmp6AZoegsW7YBPLiaOnzE_ndzf4,26686
|
|
415
|
+
sqlmesh-0.209.1.dev13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
416
|
+
sqlmesh-0.209.1.dev13.dist-info/entry_points.txt,sha256=sHAf6tQczIM8xZoduN4qaUjV7QEPVUUW_LCT8EDUMv4,155
|
|
417
|
+
sqlmesh-0.209.1.dev13.dist-info/top_level.txt,sha256=RQ-33FPe2IgL0rgossAfJkCRtqslz9b7wFARqiWLC5Q,24
|
|
418
|
+
sqlmesh-0.209.1.dev13.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|