pytrilogy 0.0.2.34__py3-none-any.whl → 0.0.2.36__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.
Potentially problematic release.
This version of pytrilogy might be problematic. Click here for more details.
- {pytrilogy-0.0.2.34.dist-info → pytrilogy-0.0.2.36.dist-info}/METADATA +2 -1
- {pytrilogy-0.0.2.34.dist-info → pytrilogy-0.0.2.36.dist-info}/RECORD +13 -13
- {pytrilogy-0.0.2.34.dist-info → pytrilogy-0.0.2.36.dist-info}/WHEEL +1 -1
- trilogy/__init__.py +1 -1
- trilogy/core/enums.py +17 -0
- trilogy/core/models.py +44 -1
- trilogy/executor.py +32 -1
- trilogy/parsing/parse_engine.py +5 -58
- trilogy/parsing/render.py +9 -3
- trilogy/parsing/trilogy.lark +2 -1
- {pytrilogy-0.0.2.34.dist-info → pytrilogy-0.0.2.36.dist-info}/LICENSE.md +0 -0
- {pytrilogy-0.0.2.34.dist-info → pytrilogy-0.0.2.36.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.2.34.dist-info → pytrilogy-0.0.2.36.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pytrilogy
|
|
3
|
-
Version: 0.0.2.
|
|
3
|
+
Version: 0.0.2.36
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -20,6 +20,7 @@ Requires-Dist: networkx
|
|
|
20
20
|
Requires-Dist: pyodbc
|
|
21
21
|
Requires-Dist: pydantic
|
|
22
22
|
Requires-Dist: duckdb-engine
|
|
23
|
+
Requires-Dist: click
|
|
23
24
|
Provides-Extra: bigquery
|
|
24
25
|
Requires-Dist: sqlalchemy-bigquery; extra == "bigquery"
|
|
25
26
|
Provides-Extra: postgres
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
trilogy/__init__.py,sha256=
|
|
1
|
+
trilogy/__init__.py,sha256=nmEtedZ0VhRnMAaMT0xsUW46MvwSivUw-Vq9Y6h2M70,291
|
|
2
2
|
trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
trilogy/constants.py,sha256=HQAnGUqJ5uMri7TWtqXHhz8iVWBzi2LCfRG8vKnBIB8,1269
|
|
4
4
|
trilogy/engine.py,sha256=R5ubIxYyrxRExz07aZCUfrTsoXCHQ8DKFTDsobXdWdA,1102
|
|
5
|
-
trilogy/executor.py,sha256=
|
|
5
|
+
trilogy/executor.py,sha256=0b0iEd660aR3_rJlHp73azRu5iQK6si5qEszoSwz8FU,13472
|
|
6
6
|
trilogy/parser.py,sha256=UtuqSiGiCjpMAYgo1bvNq-b7NSzCA5hzbUW31RXaMII,281
|
|
7
7
|
trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
trilogy/utility.py,sha256=zM__8r29EsyDW7K9VOHz8yvZC2bXFzh7xKy3cL7GKsk,707
|
|
9
9
|
trilogy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
trilogy/core/constants.py,sha256=7XaCpZn5mQmjTobbeBn56SzPWq9eMNDfzfsRU-fP0VE,171
|
|
11
|
-
trilogy/core/enums.py,sha256=
|
|
11
|
+
trilogy/core/enums.py,sha256=z-qBLh0YW52tkRtp6VLn6afxLfgI45r7_QfDtGH9CSw,6858
|
|
12
12
|
trilogy/core/env_processor.py,sha256=SHVB3nkidIlFc5dz-sofRMKXx66stpLQNuVdQSjC-So,2586
|
|
13
13
|
trilogy/core/environment_helpers.py,sha256=DIsoo-GcXmXVPB1JbNh8Oku25Nyef9mexPIdy2ur_sk,7159
|
|
14
14
|
trilogy/core/ergonomics.py,sha256=ASLDd0RqKWrZiG3XcKHo8nyTjaB_8xfE9t4NZ1UvGpc,1639
|
|
@@ -16,7 +16,7 @@ trilogy/core/exceptions.py,sha256=NvV_4qLOgKXbpotgRf7c8BANDEvHxlqRPaA53IThQ2o,56
|
|
|
16
16
|
trilogy/core/functions.py,sha256=IhVpt3n6wEanKHnGu3oA2w6-hKIlxWpEyz7fHN66mpo,10720
|
|
17
17
|
trilogy/core/graph_models.py,sha256=mameUTiuCajtihDw_2-W218xyJlvTusOWrEKP1yAWgk,2003
|
|
18
18
|
trilogy/core/internal.py,sha256=jNGFHKENnbMiMCtAgsnLZYVSENDK4b5ALecXFZpTDzQ,1075
|
|
19
|
-
trilogy/core/models.py,sha256=
|
|
19
|
+
trilogy/core/models.py,sha256=iP1ioJmKDS1_43TE0mIGWZGDtFPD3m8kKvXYA9lwCkg,162860
|
|
20
20
|
trilogy/core/optimization.py,sha256=VFSvJLNoCCOraip-PZUKeE4qrlxtXARjQUzJZiW-yRk,7325
|
|
21
21
|
trilogy/core/query_processor.py,sha256=mbcZlgjChrRjDHkdmMbKe-T70UpbBkJhS09MyU5a6UY,17785
|
|
22
22
|
trilogy/core/optimizations/__init__.py,sha256=bWQecbeiwiDx9LJnLsa7dkWxdbl2wcnkcTN69JyP8iI,356
|
|
@@ -70,14 +70,14 @@ trilogy/parsing/common.py,sha256=_GW9LU6_4RuUgcdcr8EE1ybCRd-7cz3idZtjHZ66pYA,101
|
|
|
70
70
|
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
71
71
|
trilogy/parsing/exceptions.py,sha256=92E5i2frv5hj9wxObJZsZqj5T6bglvPzvdvco_vW1Zk,38
|
|
72
72
|
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
73
|
-
trilogy/parsing/parse_engine.py,sha256=
|
|
74
|
-
trilogy/parsing/render.py,sha256=
|
|
75
|
-
trilogy/parsing/trilogy.lark,sha256=
|
|
73
|
+
trilogy/parsing/parse_engine.py,sha256=Mv2GrE-7IteyK67wZBPm0iEXY2Wbnb4CPNNSD3kXn-E,63915
|
|
74
|
+
trilogy/parsing/render.py,sha256=dhHkwmZ5HNNJr-z81JDpN2TBe4Ktqarus5vtMn2-_B8,15502
|
|
75
|
+
trilogy/parsing/trilogy.lark,sha256=o3gZiqPE3FNEJjJZDeSZ-ETD3HUQDIFZaYWrVDmxSB4,12319
|
|
76
76
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
77
77
|
trilogy/scripts/trilogy.py,sha256=PHxvv6f2ODv0esyyhWxlARgra8dVhqQhYl0lTrSyVNo,3729
|
|
78
|
-
pytrilogy-0.0.2.
|
|
79
|
-
pytrilogy-0.0.2.
|
|
80
|
-
pytrilogy-0.0.2.
|
|
81
|
-
pytrilogy-0.0.2.
|
|
82
|
-
pytrilogy-0.0.2.
|
|
83
|
-
pytrilogy-0.0.2.
|
|
78
|
+
pytrilogy-0.0.2.36.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
79
|
+
pytrilogy-0.0.2.36.dist-info/METADATA,sha256=Zl-ztc3tMnv-LbxCvmAItYOrmJ-p1pIDYW0uOmRFwYI,8424
|
|
80
|
+
pytrilogy-0.0.2.36.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
|
81
|
+
pytrilogy-0.0.2.36.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
|
|
82
|
+
pytrilogy-0.0.2.36.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
83
|
+
pytrilogy-0.0.2.36.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/core/enums.py
CHANGED
|
@@ -215,11 +215,28 @@ class Boolean(Enum):
|
|
|
215
215
|
TRUE = "true"
|
|
216
216
|
FALSE = "false"
|
|
217
217
|
|
|
218
|
+
@classmethod
|
|
219
|
+
def _missing_(cls, value):
|
|
220
|
+
if value is True:
|
|
221
|
+
return Boolean.TRUE
|
|
222
|
+
elif value is False:
|
|
223
|
+
return Boolean.FALSE
|
|
224
|
+
strval = str(value)
|
|
225
|
+
if strval.lower() != strval:
|
|
226
|
+
return Boolean(strval.lower())
|
|
227
|
+
|
|
218
228
|
|
|
219
229
|
class BooleanOperator(Enum):
|
|
220
230
|
AND = "and"
|
|
221
231
|
OR = "or"
|
|
222
232
|
|
|
233
|
+
@classmethod
|
|
234
|
+
def _missing_(cls, value):
|
|
235
|
+
strval = str(value)
|
|
236
|
+
if strval.lower() != strval:
|
|
237
|
+
return BooleanOperator(strval.lower())
|
|
238
|
+
return None
|
|
239
|
+
|
|
223
240
|
|
|
224
241
|
class ComparisonOperator(Enum):
|
|
225
242
|
LT = "<"
|
trilogy/core/models.py
CHANGED
|
@@ -1629,6 +1629,45 @@ class SelectStatement(HasUUID, Mergeable, Namespaced, SelectTypeMixin, BaseModel
|
|
|
1629
1629
|
self.grain
|
|
1630
1630
|
)
|
|
1631
1631
|
|
|
1632
|
+
def validate_syntax(self):
|
|
1633
|
+
all_in_output = [x.address for x in self.output_components]
|
|
1634
|
+
if self.where_clause:
|
|
1635
|
+
for concept in self.where_clause.concept_arguments:
|
|
1636
|
+
|
|
1637
|
+
if (
|
|
1638
|
+
concept.lineage
|
|
1639
|
+
and isinstance(concept.lineage, Function)
|
|
1640
|
+
and concept.lineage.operator
|
|
1641
|
+
in FunctionClass.AGGREGATE_FUNCTIONS.value
|
|
1642
|
+
):
|
|
1643
|
+
if concept.address in self.locally_derived:
|
|
1644
|
+
raise SyntaxError(
|
|
1645
|
+
f"Cannot reference an aggregate derived in the select ({concept.address}) in the same statement where clause; move to the HAVING clause instead; Line: {self.meta.line_number}"
|
|
1646
|
+
)
|
|
1647
|
+
|
|
1648
|
+
if (
|
|
1649
|
+
concept.lineage
|
|
1650
|
+
and isinstance(concept.lineage, AggregateWrapper)
|
|
1651
|
+
and concept.lineage.function.operator
|
|
1652
|
+
in FunctionClass.AGGREGATE_FUNCTIONS.value
|
|
1653
|
+
):
|
|
1654
|
+
if concept.address in self.locally_derived:
|
|
1655
|
+
raise SyntaxError(
|
|
1656
|
+
f"Cannot reference an aggregate derived in the select ({concept.address}) in the same statement where clause; move to the HAVING clause instead; Line: {self.meta.line_number}"
|
|
1657
|
+
)
|
|
1658
|
+
if self.having_clause:
|
|
1659
|
+
for concept in self.having_clause.concept_arguments:
|
|
1660
|
+
if concept.address not in [x.address for x in self.output_components]:
|
|
1661
|
+
raise SyntaxError(
|
|
1662
|
+
f"Cannot reference a column ({concept.address}) that is not in the select projection in the HAVING clause, move to WHERE; Line: {self.meta.line_number}"
|
|
1663
|
+
)
|
|
1664
|
+
if self.order_by:
|
|
1665
|
+
for concept in self.order_by.concept_arguments:
|
|
1666
|
+
if concept.address not in all_in_output:
|
|
1667
|
+
raise SyntaxError(
|
|
1668
|
+
f"Cannot order by a column that is not in the output projection; {self.meta.line_number}"
|
|
1669
|
+
)
|
|
1670
|
+
|
|
1632
1671
|
def __str__(self):
|
|
1633
1672
|
from trilogy.parsing.render import render_query
|
|
1634
1673
|
|
|
@@ -3237,7 +3276,7 @@ class EnvironmentConceptDict(dict):
|
|
|
3237
3276
|
return default
|
|
3238
3277
|
|
|
3239
3278
|
def __getitem__(
|
|
3240
|
-
self, key, line_no: int | None = None
|
|
3279
|
+
self, key, line_no: int | None = None, file: Path | None = None
|
|
3241
3280
|
) -> Concept | UndefinedConcept:
|
|
3242
3281
|
try:
|
|
3243
3282
|
return super(EnvironmentConceptDict, self).__getitem__(key)
|
|
@@ -3265,6 +3304,10 @@ class EnvironmentConceptDict(dict):
|
|
|
3265
3304
|
message += f" Suggestions: {matches}"
|
|
3266
3305
|
|
|
3267
3306
|
if line_no:
|
|
3307
|
+
if file:
|
|
3308
|
+
raise UndefinedConceptException(
|
|
3309
|
+
f"{file}: {line_no}: " + message, matches
|
|
3310
|
+
)
|
|
3268
3311
|
raise UndefinedConceptException(f"line: {line_no}: " + message, matches)
|
|
3269
3312
|
raise UndefinedConceptException(message, matches)
|
|
3270
3313
|
|
trilogy/executor.py
CHANGED
|
@@ -20,6 +20,8 @@ from trilogy.core.models import (
|
|
|
20
20
|
ConceptDeclarationStatement,
|
|
21
21
|
Datasource,
|
|
22
22
|
CopyStatement,
|
|
23
|
+
ImportStatement,
|
|
24
|
+
MergeStatementV2,
|
|
23
25
|
)
|
|
24
26
|
from trilogy.dialect.base import BaseDialect
|
|
25
27
|
from trilogy.dialect.enums import Dialects
|
|
@@ -104,6 +106,7 @@ class Executor(object):
|
|
|
104
106
|
ProcessedShowStatement,
|
|
105
107
|
ProcessedQueryPersist,
|
|
106
108
|
ProcessedCopyStatement,
|
|
109
|
+
ProcessedRawSQLStatement,
|
|
107
110
|
),
|
|
108
111
|
):
|
|
109
112
|
return None
|
|
@@ -142,7 +145,6 @@ class Executor(object):
|
|
|
142
145
|
|
|
143
146
|
@execute_query.register
|
|
144
147
|
def _(self, query: str) -> CursorResult:
|
|
145
|
-
|
|
146
148
|
return self.execute_text(query)[-1]
|
|
147
149
|
|
|
148
150
|
@execute_query.register
|
|
@@ -181,6 +183,35 @@ class Executor(object):
|
|
|
181
183
|
],
|
|
182
184
|
)
|
|
183
185
|
|
|
186
|
+
@execute_query.register
|
|
187
|
+
def _(self, query: ImportStatement) -> CursorResult:
|
|
188
|
+
self.environment.add_file_import(query.path, query.alias)
|
|
189
|
+
return MockResult(
|
|
190
|
+
[
|
|
191
|
+
{
|
|
192
|
+
"path": query.path,
|
|
193
|
+
"alias": query.alias,
|
|
194
|
+
}
|
|
195
|
+
],
|
|
196
|
+
["path", "alias"],
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
@execute_query.register
|
|
200
|
+
def _(self, query: MergeStatementV2) -> CursorResult:
|
|
201
|
+
|
|
202
|
+
self.environment.merge_concept(
|
|
203
|
+
query.source, query.target, modifiers=query.modifiers
|
|
204
|
+
)
|
|
205
|
+
return MockResult(
|
|
206
|
+
[
|
|
207
|
+
{
|
|
208
|
+
"source": query.source.address,
|
|
209
|
+
"target": query.target.address,
|
|
210
|
+
}
|
|
211
|
+
],
|
|
212
|
+
["source", "target"],
|
|
213
|
+
)
|
|
214
|
+
|
|
184
215
|
@execute_query.register
|
|
185
216
|
def _(self, query: ProcessedRawSQLStatement) -> CursorResult:
|
|
186
217
|
return self.execute_raw_sql(query.text)
|
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -30,7 +30,6 @@ from trilogy.core.enums import (
|
|
|
30
30
|
WindowType,
|
|
31
31
|
DatePart,
|
|
32
32
|
ShowCategory,
|
|
33
|
-
FunctionClass,
|
|
34
33
|
IOType,
|
|
35
34
|
ConceptSource,
|
|
36
35
|
)
|
|
@@ -264,7 +263,6 @@ class ParseToObjects(Transformer):
|
|
|
264
263
|
for k, v in self.parsed.items():
|
|
265
264
|
if v.pass_count == 2:
|
|
266
265
|
continue
|
|
267
|
-
print(f"Hydrating {k}")
|
|
268
266
|
v.hydrate_missing()
|
|
269
267
|
self.environment.concepts.fail_on_missing = True
|
|
270
268
|
reparsed = self.transform(self.tokens[self.token_address])
|
|
@@ -304,6 +302,9 @@ class ParseToObjects(Transformer):
|
|
|
304
302
|
def IDENTIFIER(self, args) -> str:
|
|
305
303
|
return args.value
|
|
306
304
|
|
|
305
|
+
def QUOTED_IDENTIFIER(self, args) -> str:
|
|
306
|
+
return args.value[1:-1]
|
|
307
|
+
|
|
307
308
|
@v_args(meta=True)
|
|
308
309
|
def concept_lit(self, meta: Meta, args) -> Concept:
|
|
309
310
|
return self.environment.concepts.__getitem__(args[0], meta.line)
|
|
@@ -388,7 +389,7 @@ class ParseToObjects(Transformer):
|
|
|
388
389
|
modifiers += concept_list[:-1]
|
|
389
390
|
concept = concept_list[-1]
|
|
390
391
|
resolved = self.environment.concepts.__getitem__( # type: ignore
|
|
391
|
-
key=concept, line_no=meta.line
|
|
392
|
+
key=concept, line_no=meta.line, file=self.token_address
|
|
392
393
|
)
|
|
393
394
|
return ColumnAssignment(alias=alias, modifiers=modifiers, concept=resolved)
|
|
394
395
|
|
|
@@ -988,8 +989,6 @@ class ParseToObjects(Transformer):
|
|
|
988
989
|
order_by=order_by,
|
|
989
990
|
meta=Metadata(line_number=meta.line),
|
|
990
991
|
)
|
|
991
|
-
locally_derived: set[str] = set()
|
|
992
|
-
all_in_output: set[str] = set()
|
|
993
992
|
for item in select_items:
|
|
994
993
|
# we don't know the grain of an aggregate at assignment time
|
|
995
994
|
# so rebuild at this point in the tree
|
|
@@ -998,25 +997,16 @@ class ParseToObjects(Transformer):
|
|
|
998
997
|
new_concept = item.content.output.with_select_context(
|
|
999
998
|
output.grain,
|
|
1000
999
|
conditional=None,
|
|
1001
|
-
# conditional=(
|
|
1002
|
-
# output.where_clause.conditional
|
|
1003
|
-
# if output.where_clause
|
|
1004
|
-
# and output.where_clause_category == SelectFiltering.IMPLICIT
|
|
1005
|
-
# else None
|
|
1006
|
-
# ),
|
|
1007
1000
|
environment=self.environment,
|
|
1008
1001
|
)
|
|
1009
1002
|
self.environment.add_concept(new_concept, meta=meta)
|
|
1010
1003
|
item.content.output = new_concept
|
|
1011
|
-
locally_derived.add(new_concept.address)
|
|
1012
|
-
all_in_output.add(new_concept.address)
|
|
1013
1004
|
elif isinstance(item.content, Concept):
|
|
1014
1005
|
# Sometimes cached values here don't have the latest info
|
|
1015
1006
|
# but we can't just use environment, as it might not have the right grain.
|
|
1016
1007
|
item.content = self.environment.concepts[
|
|
1017
1008
|
item.content.address
|
|
1018
1009
|
].with_grain(item.content.grain)
|
|
1019
|
-
all_in_output.add(item.content.address)
|
|
1020
1010
|
if order_by:
|
|
1021
1011
|
for orderitem in order_by.items:
|
|
1022
1012
|
if isinstance(orderitem.expr, Concept):
|
|
@@ -1024,52 +1014,9 @@ class ParseToObjects(Transformer):
|
|
|
1024
1014
|
orderitem.expr = orderitem.expr.with_select_context(
|
|
1025
1015
|
output.grain,
|
|
1026
1016
|
conditional=None,
|
|
1027
|
-
# conditional=(
|
|
1028
|
-
# output.where_clause.conditional
|
|
1029
|
-
# if output.where_clause
|
|
1030
|
-
# and output.where_clause_category
|
|
1031
|
-
# == SelectFiltering.IMPLICIT
|
|
1032
|
-
# else None
|
|
1033
|
-
# ),
|
|
1034
1017
|
environment=self.environment,
|
|
1035
1018
|
)
|
|
1036
|
-
|
|
1037
|
-
for concept in output.where_clause.concept_arguments:
|
|
1038
|
-
|
|
1039
|
-
if (
|
|
1040
|
-
concept.lineage
|
|
1041
|
-
and isinstance(concept.lineage, Function)
|
|
1042
|
-
and concept.lineage.operator
|
|
1043
|
-
in FunctionClass.AGGREGATE_FUNCTIONS.value
|
|
1044
|
-
):
|
|
1045
|
-
if concept.address in locally_derived:
|
|
1046
|
-
raise SyntaxError(
|
|
1047
|
-
f"Cannot reference an aggregate derived in the select ({concept.address}) in the same statement where clause; move to the HAVING clause instead; Line: {meta.line}"
|
|
1048
|
-
)
|
|
1049
|
-
|
|
1050
|
-
if (
|
|
1051
|
-
concept.lineage
|
|
1052
|
-
and isinstance(concept.lineage, AggregateWrapper)
|
|
1053
|
-
and concept.lineage.function.operator
|
|
1054
|
-
in FunctionClass.AGGREGATE_FUNCTIONS.value
|
|
1055
|
-
):
|
|
1056
|
-
if concept.address in locally_derived:
|
|
1057
|
-
raise SyntaxError(
|
|
1058
|
-
f"Cannot reference an aggregate derived in the select ({concept.address}) in the same statement where clause; move to the HAVING clause instead; Line: {meta.line}"
|
|
1059
|
-
)
|
|
1060
|
-
if output.having_clause:
|
|
1061
|
-
for concept in output.having_clause.concept_arguments:
|
|
1062
|
-
if concept.address not in all_in_output:
|
|
1063
|
-
raise SyntaxError(
|
|
1064
|
-
f"Cannot reference a column ({concept.address}) that is not in the select projection in the HAVING clause, move to WHERE; Line: {meta.line}"
|
|
1065
|
-
)
|
|
1066
|
-
if output.order_by:
|
|
1067
|
-
for concept in output.order_by.concept_arguments:
|
|
1068
|
-
if concept.address not in all_in_output:
|
|
1069
|
-
raise SyntaxError(
|
|
1070
|
-
f"Cannot order by a column that is not in the output projection; {meta.line}"
|
|
1071
|
-
)
|
|
1072
|
-
|
|
1019
|
+
output.validate_syntax()
|
|
1073
1020
|
return output
|
|
1074
1021
|
|
|
1075
1022
|
@v_args(meta=True)
|
trilogy/parsing/render.py
CHANGED
|
@@ -368,9 +368,15 @@ class Renderer:
|
|
|
368
368
|
|
|
369
369
|
@to_string.register
|
|
370
370
|
def _(self, arg: "ImportStatement"):
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
371
|
+
path: str = str(arg.path).replace("\\", ".")
|
|
372
|
+
path = path.replace("/", ".")
|
|
373
|
+
if path.endswith(".preql"):
|
|
374
|
+
path = path.rsplit(".", 1)[0]
|
|
375
|
+
if path.startswith("."):
|
|
376
|
+
path = path[1:]
|
|
377
|
+
if arg.alias == DEFAULT_NAMESPACE or not arg.alias:
|
|
378
|
+
return f"import {path};"
|
|
379
|
+
return f"import {path} as {arg.alias};"
|
|
374
380
|
|
|
375
381
|
@to_string.register
|
|
376
382
|
def _(self, arg: "Concept"):
|
trilogy/parsing/trilogy.lark
CHANGED
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
|
|
50
50
|
//column_assignment
|
|
51
51
|
//figure out if we want static
|
|
52
|
-
column_assignment: (raw_column_assignment | IDENTIFIER | _static_functions ) ":" concept_assignment
|
|
52
|
+
column_assignment: (raw_column_assignment | IDENTIFIER | QUOTED_IDENTIFIER | _static_functions ) ":" concept_assignment
|
|
53
53
|
|
|
54
54
|
RAW_ENTRY.1: /raw\s*\(/s
|
|
55
55
|
|
|
@@ -292,6 +292,7 @@
|
|
|
292
292
|
// base language constructs
|
|
293
293
|
concept_lit: IDENTIFIER
|
|
294
294
|
IDENTIFIER: /[a-zA-Z\_][a-zA-Z0-9\_\-\.]*/
|
|
295
|
+
QUOTED_IDENTIFIER: /`[a-zA-Z\_][a-zA-Z0-9\_\.\-\*\:\s]*`/
|
|
295
296
|
QUOTED_ADDRESS: /`[a-zA-Z\_][a-zA-Z0-9\_\.\-\*\:]*`/
|
|
296
297
|
ADDRESS: IDENTIFIER
|
|
297
298
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|