metricflow-sql 0.2.0__tar.gz → 0.3.1__tar.gz
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.
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/PKG-INFO +1 -1
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/pyproject.toml +1 -1
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/compiler.py +10 -12
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/rules/expand_constellations.py +3 -2
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/rules/expand_datasets.py +4 -3
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/server/compile.py +7 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/utils/normalize.py +10 -4
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/.gitignore +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/LICENSE +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/PYPI.md +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/README.md +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/__init__.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/__init__.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/catalog/__init__.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/catalog/cluster.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/catalog/information_schema.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/catalog/metadata.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/catalog/passthrough.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/catalog/protocols.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/catalog/semantic.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/rules/__init__.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/rules/expand_metadata.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/rules/isolate_fragments.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/rules/passthrough_tables.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/table_arguments.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/converters/__init__.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/converters/lookml.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/objects/__init__.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/objects/base.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/objects/constellation.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/objects/dataset.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/objects/entity.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/objects/reference.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/objects/wiki.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/py.typed +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/registry/__init__.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/registry/memory.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/registry/operation.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/registry/reference_resolution.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/registry/registry.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/registry/type_resolver.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/schemas/__init__.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/schemas/bundle.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/schemas/fragment.schema.json +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/schemas/generate.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/schemas/load.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/schemas/v1/__init__.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/schemas/v1/fragment.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/server/__init__.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/server/app.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/server/errors.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/server/http.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/server/registry.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/server/serde.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/server/serve.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/utils/__init__.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/utils/composed_set.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/utils/ordered_set.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/utils/sql.py +0 -0
- {metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/utils/template.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "metricflow-sql"
|
|
3
3
|
# A placeholder: a release sets its tag's version, with scripts/set_version.py.
|
|
4
|
-
version = "0.
|
|
4
|
+
version = "0.3.1"
|
|
5
5
|
description = "MetricFlow's core semantic layer engine"
|
|
6
6
|
# Its PyPI page: README.md is for this repository.
|
|
7
7
|
readme = "PYPI.md"
|
|
@@ -28,6 +28,7 @@ from metricflow_sql.compiler.rules.expand_metadata import ENGINE, ExpandMetadata
|
|
|
28
28
|
from metricflow_sql.compiler.rules.isolate_fragments import isolate_fragments
|
|
29
29
|
from metricflow_sql.compiler.rules.passthrough_tables import name_passthrough_tables
|
|
30
30
|
from metricflow_sql.compiler.table_arguments import bind_table_arguments
|
|
31
|
+
from metricflow_sql.utils.normalize import normalizing_dialect
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
def qualify_tables(
|
|
@@ -97,8 +98,8 @@ class Compiler:
|
|
|
97
98
|
planner_rules: Sequence[OptimizerFn] = PLANNER_RULES,
|
|
98
99
|
catalog: str | None = None,
|
|
99
100
|
db: str | None = None,
|
|
100
|
-
input_dialect:
|
|
101
|
-
output_dialect:
|
|
101
|
+
input_dialect: DialectType = None,
|
|
102
|
+
output_dialect: DialectType = None,
|
|
102
103
|
engine_dialects: Mapping[str, str] | None = None,
|
|
103
104
|
normalization_strategy: NormalizationStrategy | None = None,
|
|
104
105
|
) -> None:
|
|
@@ -122,8 +123,8 @@ class Compiler:
|
|
|
122
123
|
sql: str,
|
|
123
124
|
*,
|
|
124
125
|
read_only: bool = False,
|
|
125
|
-
input_dialect:
|
|
126
|
-
output_dialect:
|
|
126
|
+
input_dialect: DialectType = None,
|
|
127
|
+
output_dialect: DialectType = None,
|
|
127
128
|
positional_columns: bool = False,
|
|
128
129
|
with_tables: bool = False,
|
|
129
130
|
db: str | None = None,
|
|
@@ -150,10 +151,7 @@ class Compiler:
|
|
|
150
151
|
# dialect and normalization strategy.
|
|
151
152
|
default_db = self.db if db is None else exp.to_identifier(db).sql(dialect=input_dialect)
|
|
152
153
|
if self.normalization_strategy is not None:
|
|
153
|
-
input_dialect = (
|
|
154
|
-
f"{input_dialect}, normalization_strategy = "
|
|
155
|
-
f"{self.normalization_strategy.value.lower()}"
|
|
156
|
-
)
|
|
154
|
+
input_dialect = normalizing_dialect(input_dialect, self.normalization_strategy)
|
|
157
155
|
expressions = parse(sql, read=input_dialect)
|
|
158
156
|
explain = False
|
|
159
157
|
match expressions:
|
|
@@ -211,7 +209,7 @@ class Compiler:
|
|
|
211
209
|
tables=tuple(tables) if with_tables else None,
|
|
212
210
|
)
|
|
213
211
|
|
|
214
|
-
def _apply_rules(self, expression: exp.Expr, dialect:
|
|
212
|
+
def _apply_rules(self, expression: exp.Expr, dialect: DialectType, db: str | None) -> exp.Expr:
|
|
215
213
|
arguments = {
|
|
216
214
|
"schema": self.cluster,
|
|
217
215
|
"cluster": self.cluster,
|
|
@@ -226,7 +224,7 @@ class Compiler:
|
|
|
226
224
|
break
|
|
227
225
|
return expression
|
|
228
226
|
|
|
229
|
-
def _dialect(self, engine: str | None, output_dialect:
|
|
227
|
+
def _dialect(self, engine: str | None, output_dialect: DialectType) -> DialectType:
|
|
230
228
|
if engine is None:
|
|
231
229
|
return output_dialect
|
|
232
230
|
if engine not in self.engine_dialects:
|
|
@@ -234,7 +232,7 @@ class Compiler:
|
|
|
234
232
|
return self.engine_dialects[engine]
|
|
235
233
|
|
|
236
234
|
def _prepare(
|
|
237
|
-
self, expression: exp.Expr, dialect:
|
|
235
|
+
self, expression: exp.Expr, dialect: DialectType, db: str | None, *, read_only: bool
|
|
238
236
|
) -> None:
|
|
239
237
|
for node in expression.walk():
|
|
240
238
|
match node:
|
|
@@ -243,7 +241,7 @@ class Compiler:
|
|
|
243
241
|
case exp.Into() if read_only:
|
|
244
242
|
raise ValueError("Only one query statement is supported")
|
|
245
243
|
|
|
246
|
-
def _is_dataset(self, table: exp.Table, dialect:
|
|
244
|
+
def _is_dataset(self, table: exp.Table, dialect: DialectType, db: str | None) -> bool:
|
|
247
245
|
"""Nothing is loaded yet, so a constellation counts too."""
|
|
248
246
|
# Not normalized yet, so normalize as `qualify` will. `normalize_identifiers` changes its
|
|
249
247
|
# argument, so the query's own catalog is copied.
|
|
@@ -5,6 +5,7 @@ from dataclasses import dataclass, field
|
|
|
5
5
|
from typing import NoReturn
|
|
6
6
|
|
|
7
7
|
from sqlglot import exp
|
|
8
|
+
from sqlglot.dialects.dialect import DialectType
|
|
8
9
|
from sqlglot.helper import find_new_name
|
|
9
10
|
from sqlglot.optimizer.qualify import qualify
|
|
10
11
|
from sqlglot.optimizer.qualify_columns import quote_identifiers
|
|
@@ -104,7 +105,7 @@ class ConstellationContext:
|
|
|
104
105
|
# Whether the query has no GROUP BY or AGG, so it reads the datasets' rows, stacked.
|
|
105
106
|
stack: bool = False
|
|
106
107
|
# The query's dialect, used to qualify branches, normalize metrics and print errors.
|
|
107
|
-
dialect:
|
|
108
|
+
dialect: DialectType = None
|
|
108
109
|
|
|
109
110
|
|
|
110
111
|
class ExpandConstellationsRule:
|
|
@@ -148,7 +149,7 @@ class ExpandConstellationsRule:
|
|
|
148
149
|
raise ValueError(message)
|
|
149
150
|
|
|
150
151
|
def __call__(
|
|
151
|
-
self, expression: exp.Expr, cluster: Cluster, dialect:
|
|
152
|
+
self, expression: exp.Expr, cluster: Cluster, dialect: DialectType = None
|
|
152
153
|
) -> exp.Expr:
|
|
153
154
|
for scope, select, table in table_scopes(expression):
|
|
154
155
|
if not (registry_and_constellation := cluster.get_constellation(table)):
|
{metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/rules/expand_datasets.py
RENAMED
|
@@ -7,6 +7,7 @@ from itertools import count
|
|
|
7
7
|
from typing import NoReturn
|
|
8
8
|
|
|
9
9
|
from sqlglot import Expr, exp
|
|
10
|
+
from sqlglot.dialects.dialect import DialectType
|
|
10
11
|
from sqlglot.errors import SchemaError
|
|
11
12
|
from sqlglot.helper import find_new_name
|
|
12
13
|
from sqlglot.optimizer.helpers import projection_has_aggregate
|
|
@@ -76,7 +77,7 @@ class ExpandContext:
|
|
|
76
77
|
# One-to-many joins, keyed by alias, mapped to the alias of the join they fan out from.
|
|
77
78
|
fanout_parents: dict[str, str] = field(default_factory=dict)
|
|
78
79
|
aggregate_grains: dict[int, str] = field(default_factory=dict)
|
|
79
|
-
dialect:
|
|
80
|
+
dialect: DialectType = None
|
|
80
81
|
# The root table's table function arguments, passed to every dataset its relationships reach.
|
|
81
82
|
arguments: dict[str, exp.Expr] = field(default_factory=dict)
|
|
82
83
|
|
|
@@ -95,7 +96,7 @@ class ExpandDatasetsRule:
|
|
|
95
96
|
message = f"{message}: {expr.sql(dialect=ctx.dialect)}"
|
|
96
97
|
raise ValueError(message)
|
|
97
98
|
|
|
98
|
-
def __call__(self, expression: Expr, cluster: Cluster, dialect:
|
|
99
|
+
def __call__(self, expression: Expr, cluster: Cluster, dialect: DialectType = None) -> Expr:
|
|
99
100
|
for scope, select, table in table_scopes(expression):
|
|
100
101
|
registry_and_dataset = cluster.get_dataset(table)
|
|
101
102
|
if not registry_and_dataset:
|
|
@@ -274,7 +275,7 @@ class ExpandDatasetsRule:
|
|
|
274
275
|
add_generated(alias)
|
|
275
276
|
return ordered
|
|
276
277
|
|
|
277
|
-
def _join_terms(self, expression: exp.Expr, dialect:
|
|
278
|
+
def _join_terms(self, expression: exp.Expr, dialect: DialectType) -> set[exp.Expr]:
|
|
278
279
|
normalized = simplify(expression, dialect=dialect).unnest()
|
|
279
280
|
if isinstance(normalized, exp.And):
|
|
280
281
|
return set(normalized.flatten())
|
|
@@ -25,6 +25,7 @@ import logging
|
|
|
25
25
|
from collections.abc import Awaitable, Callable
|
|
26
26
|
from typing import Protocol
|
|
27
27
|
|
|
28
|
+
from sqlglot.dialects.dialect import Dialect
|
|
28
29
|
from sqlglot.errors import SqlglotError
|
|
29
30
|
from starlette.requests import Request
|
|
30
31
|
from starlette.responses import JSONResponse
|
|
@@ -57,6 +58,12 @@ def compile_routes(compile_for: CompileFor) -> list[BaseRoute]:
|
|
|
57
58
|
async def post_compile(request: Request) -> JSONResponse:
|
|
58
59
|
compile_sql = compile_for(request)
|
|
59
60
|
body = await request.json()
|
|
61
|
+
for key in ("input_dialect", "output_dialect"):
|
|
62
|
+
try:
|
|
63
|
+
# Checked as sqlglot reads it, then passed on as text.
|
|
64
|
+
Dialect.get_or_raise(body.get(key))
|
|
65
|
+
except ValueError as e:
|
|
66
|
+
return JSONResponse({"error": f"Invalid `{key}`: {e}"}, status_code=400)
|
|
60
67
|
try:
|
|
61
68
|
plan = await compile_sql(
|
|
62
69
|
body["sql"],
|
|
@@ -7,11 +7,12 @@ way.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import copy
|
|
10
11
|
import logging
|
|
11
12
|
from collections.abc import Iterable
|
|
12
13
|
|
|
13
14
|
from sqlglot import exp
|
|
14
|
-
from sqlglot.dialects.dialect import Dialect, NormalizationStrategy
|
|
15
|
+
from sqlglot.dialects.dialect import Dialect, DialectType, NormalizationStrategy
|
|
15
16
|
from sqlglot.optimizer.normalize_identifiers import normalize_identifiers
|
|
16
17
|
from sqlglot.optimizer.qualify_columns import quote_identifiers
|
|
17
18
|
|
|
@@ -25,9 +26,14 @@ def strategy_of(dialect: str | None) -> NormalizationStrategy:
|
|
|
25
26
|
return Dialect.get_or_raise(dialect).normalization_strategy
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
def normalizing_dialect(dialect:
|
|
29
|
-
"""
|
|
30
|
-
|
|
29
|
+
def normalizing_dialect(dialect: DialectType, strategy: NormalizationStrategy) -> Dialect:
|
|
30
|
+
"""
|
|
31
|
+
`dialect`, reading names as `strategy` normalizes them, e.g. for sqlglot's `dialect=`. Its
|
|
32
|
+
other settings, e.g. its version, carry over; a `Dialect` given is copied, not changed.
|
|
33
|
+
"""
|
|
34
|
+
normalizing = copy.copy(Dialect.get_or_raise(dialect))
|
|
35
|
+
normalizing.normalization_strategy = strategy
|
|
36
|
+
return normalizing
|
|
31
37
|
|
|
32
38
|
|
|
33
39
|
def canonical[E: exp.Expr](
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/catalog/__init__.py
RENAMED
|
File without changes
|
{metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/catalog/cluster.py
RENAMED
|
File without changes
|
|
File without changes
|
{metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/catalog/metadata.py
RENAMED
|
File without changes
|
{metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/catalog/passthrough.py
RENAMED
|
File without changes
|
{metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/catalog/protocols.py
RENAMED
|
File without changes
|
{metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/catalog/semantic.py
RENAMED
|
File without changes
|
|
File without changes
|
{metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/rules/expand_metadata.py
RENAMED
|
File without changes
|
{metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/rules/isolate_fragments.py
RENAMED
|
File without changes
|
|
File without changes
|
{metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/compiler/table_arguments.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/registry/reference_resolution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{metricflow_sql-0.2.0 → metricflow_sql-0.3.1}/src/metricflow_sql/schemas/fragment.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|