pytrilogy 0.0.3.52__tar.gz → 0.0.3.53__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.
Potentially problematic release.
This version of pytrilogy might be problematic. Click here for more details.
- {pytrilogy-0.0.3.52/pytrilogy.egg-info → pytrilogy-0.0.3.53}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/pytrilogy.egg-info/SOURCES.txt +1 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_functions.py +3 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_parse_engine.py +30 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_parsing.py +2 -2
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_show.py +1 -1
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/enums.py +5 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/functions.py +18 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/models/execute.py +18 -13
- pytrilogy-0.0.3.53/trilogy/core/utility.py +8 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/dialect/base.py +15 -13
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/dialect/bigquery.py +1 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/dialect/common.py +5 -4
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/parsing/parse_engine.py +19 -1
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/parsing/trilogy.lark +11 -4
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/README.md +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/setup.cfg +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/setup.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_failure.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_parsing_failures.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_query_render.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_select.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_typing.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_user_functions.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/authoring/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/models/author.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/models/build.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/models/core.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/models/environment.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/concept_strategies_v3.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/statements/author.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/statements/execute.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/executor.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/parsing/common.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/render.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/std/__init__.py +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/std/date.preql +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/std/display.preql +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/std/geography.preql +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/std/money.preql +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/std/report.preql +0 -0
- {pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/utility.py +0 -0
|
@@ -261,6 +261,9 @@ def test_string_functions(test_environment):
|
|
|
261
261
|
property strpos_name <- strpos(category_name, 'a');
|
|
262
262
|
property like_name <- like(category_name, 'a%');
|
|
263
263
|
property like_alt <- category_name like 'a%';
|
|
264
|
+
property regex_contains <- regexp_contains(category_name, 'a');
|
|
265
|
+
property regex_substring <- regexp_extract(category_name, 'a');
|
|
266
|
+
property regex_replace <- regexp_replace(category_name, 'a', 'b');
|
|
264
267
|
|
|
265
268
|
select
|
|
266
269
|
test_name,
|
|
@@ -46,6 +46,36 @@ FROM a
|
|
|
46
46
|
"""
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
def test_parse_datatype_in_datasource():
|
|
50
|
+
env = Environment()
|
|
51
|
+
x = ParseToObjects(environment=env)
|
|
52
|
+
test_text = """
|
|
53
|
+
key x int;
|
|
54
|
+
property x.timestamp timestamp;
|
|
55
|
+
|
|
56
|
+
datasource funky (
|
|
57
|
+
x: x,
|
|
58
|
+
timestamp:timestamp)
|
|
59
|
+
address fun;
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
x.set_text(test_text)
|
|
63
|
+
|
|
64
|
+
tokens = PARSER.parse(test_text)
|
|
65
|
+
x.transform(tokens)
|
|
66
|
+
x.run_second_parse_pass()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
TEXT2 = """
|
|
70
|
+
const a <- 1;
|
|
71
|
+
|
|
72
|
+
select
|
|
73
|
+
a,
|
|
74
|
+
FROM a
|
|
75
|
+
;
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
|
|
49
79
|
def test_from_error():
|
|
50
80
|
env = Environment()
|
|
51
81
|
|
|
@@ -496,7 +496,7 @@ select x;
|
|
|
496
496
|
|
|
497
497
|
results = Dialects.DUCK_DB.default_executor().generate_sql(text)[0]
|
|
498
498
|
|
|
499
|
-
assert '"abc:def" as test' in results
|
|
499
|
+
assert '"abc:def" as "test"' in results
|
|
500
500
|
|
|
501
501
|
text = """
|
|
502
502
|
key x int;
|
|
@@ -516,7 +516,7 @@ select x;
|
|
|
516
516
|
|
|
517
517
|
results = Dialects.DUCK_DB.default_executor().generate_sql(text)[0]
|
|
518
518
|
|
|
519
|
-
assert "abcdef as test" in results, results
|
|
519
|
+
assert '"abcdef" as "test"' in results, results
|
|
520
520
|
|
|
521
521
|
|
|
522
522
|
def test_datasource_where_equivalent():
|
|
@@ -181,6 +181,11 @@ class FunctionType(Enum):
|
|
|
181
181
|
STRPOS = "strpos"
|
|
182
182
|
CONTAINS = "contains"
|
|
183
183
|
|
|
184
|
+
# STRING REGEX
|
|
185
|
+
REGEXP_CONTAINS = "regexp_contains"
|
|
186
|
+
REGEXP_EXTRACT = "regexp_extract"
|
|
187
|
+
REGEXP_REPLACE = "regexp_replace"
|
|
188
|
+
|
|
184
189
|
# Dates
|
|
185
190
|
DATE = "date"
|
|
186
191
|
DATETIME = "datetime"
|
|
@@ -360,6 +360,24 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
360
360
|
output_type=DataType.STRING,
|
|
361
361
|
arg_count=1,
|
|
362
362
|
),
|
|
363
|
+
FunctionType.REGEXP_CONTAINS: FunctionConfig(
|
|
364
|
+
valid_inputs={DataType.STRING},
|
|
365
|
+
output_purpose=Purpose.PROPERTY,
|
|
366
|
+
output_type=DataType.BOOL,
|
|
367
|
+
arg_count=2,
|
|
368
|
+
),
|
|
369
|
+
FunctionType.REGEXP_EXTRACT: FunctionConfig(
|
|
370
|
+
valid_inputs={DataType.STRING},
|
|
371
|
+
output_purpose=Purpose.PROPERTY,
|
|
372
|
+
output_type=DataType.STRING,
|
|
373
|
+
arg_count=2,
|
|
374
|
+
),
|
|
375
|
+
FunctionType.REGEXP_REPLACE: FunctionConfig(
|
|
376
|
+
valid_inputs={DataType.STRING},
|
|
377
|
+
output_purpose=Purpose.PROPERTY,
|
|
378
|
+
output_type=DataType.STRING,
|
|
379
|
+
arg_count=3,
|
|
380
|
+
),
|
|
363
381
|
FunctionType.DATE: FunctionConfig(
|
|
364
382
|
valid_inputs={
|
|
365
383
|
DataType.DATE,
|
|
@@ -37,6 +37,7 @@ from trilogy.core.models.build import (
|
|
|
37
37
|
LooseBuildConceptList,
|
|
38
38
|
)
|
|
39
39
|
from trilogy.core.models.datasource import Address
|
|
40
|
+
from trilogy.core.utility import safe_quote
|
|
40
41
|
from trilogy.utility import unique
|
|
41
42
|
|
|
42
43
|
LOGGER_PREFIX = "[MODELS_EXECUTE]"
|
|
@@ -201,6 +202,7 @@ class CTE(BaseModel):
|
|
|
201
202
|
self.base_name_override = ds_being_inlined.safe_location
|
|
202
203
|
self.base_alias_override = ds_being_inlined.safe_identifier
|
|
203
204
|
|
|
205
|
+
# if we have a join to the parent, we need to remove it
|
|
204
206
|
for join in self.joins:
|
|
205
207
|
if isinstance(join, InstantiatedUnnestJoin):
|
|
206
208
|
continue
|
|
@@ -322,17 +324,20 @@ class CTE(BaseModel):
|
|
|
322
324
|
return self.source.name
|
|
323
325
|
|
|
324
326
|
@property
|
|
325
|
-
def quote_address(self) ->
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
327
|
+
def quote_address(self) -> bool:
|
|
328
|
+
if self.is_root_datasource:
|
|
329
|
+
root = self.source.datasources[0]
|
|
330
|
+
if isinstance(root, BuildDatasource) and isinstance(root.address, Address):
|
|
331
|
+
return not root.address.is_query
|
|
332
|
+
return True
|
|
333
|
+
elif not self.source.datasources:
|
|
334
|
+
return False
|
|
335
|
+
base = self.source.datasources[0]
|
|
336
|
+
if isinstance(base, BuildDatasource):
|
|
337
|
+
if isinstance(base.address, Address):
|
|
338
|
+
return not base.address.is_query
|
|
339
|
+
return True
|
|
340
|
+
return True
|
|
336
341
|
|
|
337
342
|
@property
|
|
338
343
|
def base_alias(self) -> str:
|
|
@@ -926,8 +931,8 @@ class Join(BaseModel):
|
|
|
926
931
|
def right_ref(self) -> str:
|
|
927
932
|
if self.quote:
|
|
928
933
|
if self.right_cte.identifier in self.inlined_ctes:
|
|
929
|
-
return f"{self.
|
|
930
|
-
return self.right_cte.safe_identifier
|
|
934
|
+
return f"{safe_quote(self.right_cte.source.datasources[0].safe_location, self.quote)} as {self.quote}{self.right_cte.source.datasources[0].safe_identifier}{self.quote}"
|
|
935
|
+
return f"{self.quote}{self.right_cte.safe_identifier}{self.quote}"
|
|
931
936
|
if self.right_cte.identifier in self.inlined_ctes:
|
|
932
937
|
return f"{self.right_cte.source.datasources[0].safe_location} as {self.right_cte.source.datasources[0].safe_identifier}"
|
|
933
938
|
return self.right_cte.safe_identifier
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
def safe_quote(string: str, quote_char: str):
|
|
2
|
+
# split dotted identifiers
|
|
3
|
+
# TODO: evaluate if we need smarter parsing for strings that could actually include .
|
|
4
|
+
if string.startswith("https://"):
|
|
5
|
+
# it's a url, no splitting
|
|
6
|
+
return f"{quote_char}{string}{quote_char}"
|
|
7
|
+
components = string.split(".")
|
|
8
|
+
return ".".join([f"{quote_char}{string}{quote_char}" for string in components])
|
|
@@ -74,6 +74,7 @@ from trilogy.core.statements.execute import (
|
|
|
74
74
|
ProcessedRawSQLStatement,
|
|
75
75
|
ProcessedShowStatement,
|
|
76
76
|
)
|
|
77
|
+
from trilogy.core.utility import safe_quote
|
|
77
78
|
from trilogy.dialect.common import render_join, render_unnest
|
|
78
79
|
from trilogy.hooks.base_hook import BaseHook
|
|
79
80
|
|
|
@@ -204,6 +205,9 @@ FUNCTION_MAP = {
|
|
|
204
205
|
FunctionType.SUBSTRING: lambda x: f"SUBSTRING({x[0]},{x[1]},{x[2]})",
|
|
205
206
|
FunctionType.STRPOS: lambda x: f"STRPOS({x[0]},{x[1]})",
|
|
206
207
|
FunctionType.CONTAINS: lambda x: f"CONTAINS({x[0]},{x[1]})",
|
|
208
|
+
FunctionType.REGEXP_CONTAINS: lambda x: f"REGEXP_CONTAINS({x[0]},{x[1]})",
|
|
209
|
+
FunctionType.REGEXP_EXTRACT: lambda x: f"REGEXP_EXTRACT({x[0]},{x[1]})",
|
|
210
|
+
FunctionType.REGEXP_REPLACE: lambda x: f"REGEXP_REPLACE({x[0]},{x[1]}, {x[2]})",
|
|
207
211
|
# FunctionType.NOT_LIKE: lambda x: f" CASE WHEN {x[0]} like {x[1]} THEN 0 ELSE 1 END",
|
|
208
212
|
# date types
|
|
209
213
|
FunctionType.DATE_TRUNCATE: lambda x: f"date_trunc({x[0]},{x[1]})",
|
|
@@ -270,13 +274,6 @@ ORDER BY{% for order in order_by %}
|
|
|
270
274
|
)
|
|
271
275
|
|
|
272
276
|
|
|
273
|
-
def safe_quote(string: str, quote_char: str):
|
|
274
|
-
# split dotted identifiers
|
|
275
|
-
# TODO: evaluate if we need smarter parsing for strings that could actually include .
|
|
276
|
-
components = string.split(".")
|
|
277
|
-
return ".".join([f"{quote_char}{string}{quote_char}" for string in components])
|
|
278
|
-
|
|
279
|
-
|
|
280
277
|
def safe_get_cte_value(coalesce, cte: CTE | UnionCTE, c: BuildConcept, quote_char: str):
|
|
281
278
|
address = c.address
|
|
282
279
|
raw = cte.source_map.get(address, None)
|
|
@@ -285,12 +282,17 @@ def safe_get_cte_value(coalesce, cte: CTE | UnionCTE, c: BuildConcept, quote_cha
|
|
|
285
282
|
return None
|
|
286
283
|
if isinstance(raw, str):
|
|
287
284
|
rendered = cte.get_alias(c, raw)
|
|
288
|
-
return f"{raw}.{safe_quote(rendered, quote_char)}"
|
|
285
|
+
return f"{quote_char}{raw}{quote_char}.{safe_quote(rendered, quote_char)}"
|
|
289
286
|
if isinstance(raw, list) and len(raw) == 1:
|
|
290
287
|
rendered = cte.get_alias(c, raw[0])
|
|
291
|
-
return f"{raw[0]}.{safe_quote(rendered, quote_char)}"
|
|
288
|
+
return f"{quote_char}{raw[0]}{quote_char}.{safe_quote(rendered, quote_char)}"
|
|
292
289
|
return coalesce(
|
|
293
|
-
sorted(
|
|
290
|
+
sorted(
|
|
291
|
+
[
|
|
292
|
+
f"{quote_char}{x}{quote_char}.{safe_quote(cte.get_alias(c, x), quote_char)}"
|
|
293
|
+
for x in raw
|
|
294
|
+
]
|
|
295
|
+
)
|
|
294
296
|
)
|
|
295
297
|
|
|
296
298
|
|
|
@@ -783,12 +785,12 @@ class BaseDialect:
|
|
|
783
785
|
else:
|
|
784
786
|
source = None
|
|
785
787
|
else:
|
|
786
|
-
if cte.quote_address
|
|
787
|
-
source =
|
|
788
|
+
if cte.quote_address:
|
|
789
|
+
source = safe_quote(cte.base_name, self.QUOTE_CHARACTER)
|
|
788
790
|
else:
|
|
789
791
|
source = cte.base_name
|
|
790
792
|
if cte.base_name != cte.base_alias:
|
|
791
|
-
source = f"{source} as {cte.base_alias}"
|
|
793
|
+
source = f"{source} as {self.QUOTE_CHARACTER}{cte.base_alias}{self.QUOTE_CHARACTER}"
|
|
792
794
|
if not cte.render_from_clause:
|
|
793
795
|
final_joins = []
|
|
794
796
|
else:
|
|
@@ -18,6 +18,7 @@ FUNCTION_MAP = {
|
|
|
18
18
|
FunctionType.LIKE: lambda x: (
|
|
19
19
|
f" CASE WHEN {x[0]} like {x[1]} THEN True ELSE False END"
|
|
20
20
|
),
|
|
21
|
+
FunctionType.IS_NULL: lambda x: f"CASE WHEN {x[0]} IS NULL THEN True ELSE False END",
|
|
21
22
|
FunctionType.MINUTE: lambda x: f"EXTRACT(MINUTE from {x[0]})",
|
|
22
23
|
FunctionType.SECOND: lambda x: f"EXTRACT(SECOND from {x[0]})",
|
|
23
24
|
FunctionType.HOUR: lambda x: f"EXTRACT(HOUR from {x[0]})",
|
|
@@ -63,8 +63,8 @@ def render_join_concept(
|
|
|
63
63
|
elif isinstance(raw_content, BuildFunction):
|
|
64
64
|
rval = render_expr(raw_content, cte=cte)
|
|
65
65
|
return rval
|
|
66
|
-
return f"{name}.{quote_character}{raw_content}{quote_character}"
|
|
67
|
-
return f"{name}.{quote_character}{concept.safe_address}{quote_character}"
|
|
66
|
+
return f"{quote_character}{name}{quote_character}.{quote_character}{raw_content}{quote_character}"
|
|
67
|
+
return f"{quote_character}{name}{quote_character}.{quote_character}{concept.safe_address}{quote_character}"
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
def render_join(
|
|
@@ -91,8 +91,9 @@ def render_join(
|
|
|
91
91
|
return f"FULL JOIN {render_unnest(unnest_mode, quote_character, join.object_to_unnest, render_expr_func, cte)}"
|
|
92
92
|
# left_name = join.left_name
|
|
93
93
|
right_name = join.right_name
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
join.quote = quote_character
|
|
95
|
+
# if cte.quote_address.get(join.right_name, False):
|
|
96
|
+
# join.quote = quote_character
|
|
96
97
|
right_base = join.right_ref
|
|
97
98
|
base_joinkeys = []
|
|
98
99
|
if join.joinkey_pairs:
|
|
@@ -1744,9 +1744,27 @@ class ParseToObjects(Transformer):
|
|
|
1744
1744
|
return self.function_factory.create_function(args, FunctionType.SUBSTRING, meta)
|
|
1745
1745
|
|
|
1746
1746
|
@v_args(meta=True)
|
|
1747
|
-
def
|
|
1747
|
+
def flower(self, meta, args):
|
|
1748
1748
|
return self.function_factory.create_function(args, FunctionType.LOWER, meta)
|
|
1749
1749
|
|
|
1750
|
+
@v_args(meta=True)
|
|
1751
|
+
def fregexp_contains(self, meta, args):
|
|
1752
|
+
return self.function_factory.create_function(
|
|
1753
|
+
args, FunctionType.REGEXP_CONTAINS, meta
|
|
1754
|
+
)
|
|
1755
|
+
|
|
1756
|
+
@v_args(meta=True)
|
|
1757
|
+
def fregexp_extract(self, meta, args):
|
|
1758
|
+
return self.function_factory.create_function(
|
|
1759
|
+
args, FunctionType.REGEXP_EXTRACT, meta
|
|
1760
|
+
)
|
|
1761
|
+
|
|
1762
|
+
@v_args(meta=True)
|
|
1763
|
+
def fregexp_replace(self, meta, args):
|
|
1764
|
+
return self.function_factory.create_function(
|
|
1765
|
+
args, FunctionType.REGEXP_REPLACE, meta
|
|
1766
|
+
)
|
|
1767
|
+
|
|
1750
1768
|
# date functions
|
|
1751
1769
|
@v_args(meta=True)
|
|
1752
1770
|
def fdate(self, meta, args):
|
|
@@ -267,7 +267,7 @@
|
|
|
267
267
|
_UPPER.1: "upper("i
|
|
268
268
|
upper: _UPPER expr ")"
|
|
269
269
|
_LOWER.1: "lower("i
|
|
270
|
-
|
|
270
|
+
flower: _LOWER expr ")"
|
|
271
271
|
_SPLIT.1: "split("i
|
|
272
272
|
fsplit: _SPLIT expr "," string_lit ")"
|
|
273
273
|
_STRPOS.1: "strpos("i
|
|
@@ -276,8 +276,14 @@
|
|
|
276
276
|
fcontains: _CONTAINS expr "," expr ")"
|
|
277
277
|
_SUBSTRING.1: "substring("i
|
|
278
278
|
fsubstring: _SUBSTRING expr "," expr "," expr ")"
|
|
279
|
-
|
|
280
|
-
|
|
279
|
+
_REGEXP_EXTRACT.1: "regexp_extract("
|
|
280
|
+
fregexp_extract: _REGEXP_EXTRACT expr "," expr ")"
|
|
281
|
+
_REGEXP_CONTAINS.1: "regexp_contains("
|
|
282
|
+
fregexp_contains: _REGEXP_CONTAINS expr "," expr ")"
|
|
283
|
+
_REGEXP_REPLACE.1: "regexp_replace("
|
|
284
|
+
fregexp_replace: _REGEXP_REPLACE expr "," expr "," expr ")"
|
|
285
|
+
|
|
286
|
+
_string_functions: like | ilike | upper | flower | fsplit | fstrpos | fsubstring | fcontains | fregexp_extract | fregexp_contains | fregexp_replace
|
|
281
287
|
|
|
282
288
|
// special aggregate
|
|
283
289
|
_GROUP.1: "group("i
|
|
@@ -311,7 +317,8 @@
|
|
|
311
317
|
_DATE.1: "date("i
|
|
312
318
|
fdate: _DATE expr ")"
|
|
313
319
|
fdatetime: "datetime"i "(" expr ")"
|
|
314
|
-
|
|
320
|
+
_TIMESTAMP.1: "timestamp("i
|
|
321
|
+
ftimestamp: _TIMESTAMP expr ")"
|
|
315
322
|
|
|
316
323
|
_SECOND.1: "second("i
|
|
317
324
|
fsecond: _SECOND expr ")"
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/__init__.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/basic_node.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/filter_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/group_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/group_to_node.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/node_merge_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/rowset_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/select_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/synonym_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/union_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/unnest_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.52 → pytrilogy-0.0.3.53}/trilogy/core/processing/node_generators/window_node.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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|