pytrilogy 0.3.148__cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.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.
Files changed (206) hide show
  1. LICENSE.md +19 -0
  2. _preql_import_resolver/__init__.py +5 -0
  3. _preql_import_resolver/_preql_import_resolver.cpython-312-aarch64-linux-gnu.so +0 -0
  4. pytrilogy-0.3.148.dist-info/METADATA +555 -0
  5. pytrilogy-0.3.148.dist-info/RECORD +206 -0
  6. pytrilogy-0.3.148.dist-info/WHEEL +5 -0
  7. pytrilogy-0.3.148.dist-info/entry_points.txt +2 -0
  8. pytrilogy-0.3.148.dist-info/licenses/LICENSE.md +19 -0
  9. trilogy/__init__.py +27 -0
  10. trilogy/ai/README.md +10 -0
  11. trilogy/ai/__init__.py +19 -0
  12. trilogy/ai/constants.py +92 -0
  13. trilogy/ai/conversation.py +107 -0
  14. trilogy/ai/enums.py +7 -0
  15. trilogy/ai/execute.py +50 -0
  16. trilogy/ai/models.py +34 -0
  17. trilogy/ai/prompts.py +100 -0
  18. trilogy/ai/providers/__init__.py +0 -0
  19. trilogy/ai/providers/anthropic.py +106 -0
  20. trilogy/ai/providers/base.py +24 -0
  21. trilogy/ai/providers/google.py +146 -0
  22. trilogy/ai/providers/openai.py +89 -0
  23. trilogy/ai/providers/utils.py +68 -0
  24. trilogy/authoring/README.md +3 -0
  25. trilogy/authoring/__init__.py +148 -0
  26. trilogy/constants.py +119 -0
  27. trilogy/core/README.md +52 -0
  28. trilogy/core/__init__.py +0 -0
  29. trilogy/core/constants.py +6 -0
  30. trilogy/core/enums.py +454 -0
  31. trilogy/core/env_processor.py +239 -0
  32. trilogy/core/environment_helpers.py +320 -0
  33. trilogy/core/ergonomics.py +193 -0
  34. trilogy/core/exceptions.py +123 -0
  35. trilogy/core/functions.py +1240 -0
  36. trilogy/core/graph_models.py +142 -0
  37. trilogy/core/internal.py +85 -0
  38. trilogy/core/models/__init__.py +0 -0
  39. trilogy/core/models/author.py +2662 -0
  40. trilogy/core/models/build.py +2603 -0
  41. trilogy/core/models/build_environment.py +165 -0
  42. trilogy/core/models/core.py +506 -0
  43. trilogy/core/models/datasource.py +434 -0
  44. trilogy/core/models/environment.py +756 -0
  45. trilogy/core/models/execute.py +1213 -0
  46. trilogy/core/optimization.py +251 -0
  47. trilogy/core/optimizations/__init__.py +12 -0
  48. trilogy/core/optimizations/base_optimization.py +17 -0
  49. trilogy/core/optimizations/hide_unused_concept.py +47 -0
  50. trilogy/core/optimizations/inline_datasource.py +102 -0
  51. trilogy/core/optimizations/predicate_pushdown.py +245 -0
  52. trilogy/core/processing/README.md +94 -0
  53. trilogy/core/processing/READMEv2.md +121 -0
  54. trilogy/core/processing/VIRTUAL_UNNEST.md +30 -0
  55. trilogy/core/processing/__init__.py +0 -0
  56. trilogy/core/processing/concept_strategies_v3.py +508 -0
  57. trilogy/core/processing/constants.py +15 -0
  58. trilogy/core/processing/discovery_node_factory.py +451 -0
  59. trilogy/core/processing/discovery_utility.py +548 -0
  60. trilogy/core/processing/discovery_validation.py +167 -0
  61. trilogy/core/processing/graph_utils.py +43 -0
  62. trilogy/core/processing/node_generators/README.md +9 -0
  63. trilogy/core/processing/node_generators/__init__.py +31 -0
  64. trilogy/core/processing/node_generators/basic_node.py +160 -0
  65. trilogy/core/processing/node_generators/common.py +270 -0
  66. trilogy/core/processing/node_generators/constant_node.py +38 -0
  67. trilogy/core/processing/node_generators/filter_node.py +315 -0
  68. trilogy/core/processing/node_generators/group_node.py +213 -0
  69. trilogy/core/processing/node_generators/group_to_node.py +117 -0
  70. trilogy/core/processing/node_generators/multiselect_node.py +207 -0
  71. trilogy/core/processing/node_generators/node_merge_node.py +695 -0
  72. trilogy/core/processing/node_generators/recursive_node.py +88 -0
  73. trilogy/core/processing/node_generators/rowset_node.py +165 -0
  74. trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  75. trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +261 -0
  76. trilogy/core/processing/node_generators/select_merge_node.py +786 -0
  77. trilogy/core/processing/node_generators/select_node.py +95 -0
  78. trilogy/core/processing/node_generators/synonym_node.py +98 -0
  79. trilogy/core/processing/node_generators/union_node.py +91 -0
  80. trilogy/core/processing/node_generators/unnest_node.py +182 -0
  81. trilogy/core/processing/node_generators/window_node.py +201 -0
  82. trilogy/core/processing/nodes/README.md +28 -0
  83. trilogy/core/processing/nodes/__init__.py +179 -0
  84. trilogy/core/processing/nodes/base_node.py +522 -0
  85. trilogy/core/processing/nodes/filter_node.py +75 -0
  86. trilogy/core/processing/nodes/group_node.py +194 -0
  87. trilogy/core/processing/nodes/merge_node.py +420 -0
  88. trilogy/core/processing/nodes/recursive_node.py +46 -0
  89. trilogy/core/processing/nodes/select_node_v2.py +242 -0
  90. trilogy/core/processing/nodes/union_node.py +53 -0
  91. trilogy/core/processing/nodes/unnest_node.py +62 -0
  92. trilogy/core/processing/nodes/window_node.py +56 -0
  93. trilogy/core/processing/utility.py +823 -0
  94. trilogy/core/query_processor.py +604 -0
  95. trilogy/core/statements/README.md +35 -0
  96. trilogy/core/statements/__init__.py +0 -0
  97. trilogy/core/statements/author.py +536 -0
  98. trilogy/core/statements/build.py +0 -0
  99. trilogy/core/statements/common.py +20 -0
  100. trilogy/core/statements/execute.py +155 -0
  101. trilogy/core/table_processor.py +66 -0
  102. trilogy/core/utility.py +8 -0
  103. trilogy/core/validation/README.md +46 -0
  104. trilogy/core/validation/__init__.py +0 -0
  105. trilogy/core/validation/common.py +161 -0
  106. trilogy/core/validation/concept.py +146 -0
  107. trilogy/core/validation/datasource.py +227 -0
  108. trilogy/core/validation/environment.py +73 -0
  109. trilogy/core/validation/fix.py +256 -0
  110. trilogy/dialect/__init__.py +32 -0
  111. trilogy/dialect/base.py +1431 -0
  112. trilogy/dialect/bigquery.py +314 -0
  113. trilogy/dialect/common.py +147 -0
  114. trilogy/dialect/config.py +159 -0
  115. trilogy/dialect/dataframe.py +50 -0
  116. trilogy/dialect/duckdb.py +376 -0
  117. trilogy/dialect/enums.py +149 -0
  118. trilogy/dialect/metadata.py +173 -0
  119. trilogy/dialect/mock.py +190 -0
  120. trilogy/dialect/postgres.py +117 -0
  121. trilogy/dialect/presto.py +110 -0
  122. trilogy/dialect/results.py +89 -0
  123. trilogy/dialect/snowflake.py +129 -0
  124. trilogy/dialect/sql_server.py +137 -0
  125. trilogy/engine.py +48 -0
  126. trilogy/execution/__init__.py +17 -0
  127. trilogy/execution/config.py +119 -0
  128. trilogy/execution/state/__init__.py +0 -0
  129. trilogy/execution/state/file_state_store.py +0 -0
  130. trilogy/execution/state/sqllite_state_store.py +0 -0
  131. trilogy/execution/state/state_store.py +301 -0
  132. trilogy/executor.py +656 -0
  133. trilogy/hooks/__init__.py +4 -0
  134. trilogy/hooks/base_hook.py +40 -0
  135. trilogy/hooks/graph_hook.py +135 -0
  136. trilogy/hooks/query_debugger.py +166 -0
  137. trilogy/metadata/__init__.py +0 -0
  138. trilogy/parser.py +10 -0
  139. trilogy/parsing/README.md +21 -0
  140. trilogy/parsing/__init__.py +0 -0
  141. trilogy/parsing/common.py +1069 -0
  142. trilogy/parsing/config.py +5 -0
  143. trilogy/parsing/exceptions.py +8 -0
  144. trilogy/parsing/helpers.py +1 -0
  145. trilogy/parsing/parse_engine.py +2863 -0
  146. trilogy/parsing/render.py +773 -0
  147. trilogy/parsing/trilogy.lark +544 -0
  148. trilogy/py.typed +0 -0
  149. trilogy/render.py +45 -0
  150. trilogy/scripts/README.md +9 -0
  151. trilogy/scripts/__init__.py +0 -0
  152. trilogy/scripts/agent.py +41 -0
  153. trilogy/scripts/agent_info.py +306 -0
  154. trilogy/scripts/common.py +430 -0
  155. trilogy/scripts/dependency/Cargo.lock +617 -0
  156. trilogy/scripts/dependency/Cargo.toml +39 -0
  157. trilogy/scripts/dependency/README.md +131 -0
  158. trilogy/scripts/dependency/build.sh +25 -0
  159. trilogy/scripts/dependency/src/directory_resolver.rs +387 -0
  160. trilogy/scripts/dependency/src/lib.rs +16 -0
  161. trilogy/scripts/dependency/src/main.rs +770 -0
  162. trilogy/scripts/dependency/src/parser.rs +435 -0
  163. trilogy/scripts/dependency/src/preql.pest +208 -0
  164. trilogy/scripts/dependency/src/python_bindings.rs +311 -0
  165. trilogy/scripts/dependency/src/resolver.rs +716 -0
  166. trilogy/scripts/dependency/tests/base.preql +3 -0
  167. trilogy/scripts/dependency/tests/cli_integration.rs +377 -0
  168. trilogy/scripts/dependency/tests/customer.preql +6 -0
  169. trilogy/scripts/dependency/tests/main.preql +9 -0
  170. trilogy/scripts/dependency/tests/orders.preql +7 -0
  171. trilogy/scripts/dependency/tests/test_data/base.preql +9 -0
  172. trilogy/scripts/dependency/tests/test_data/consumer.preql +1 -0
  173. trilogy/scripts/dependency.py +323 -0
  174. trilogy/scripts/display.py +555 -0
  175. trilogy/scripts/environment.py +59 -0
  176. trilogy/scripts/fmt.py +32 -0
  177. trilogy/scripts/ingest.py +472 -0
  178. trilogy/scripts/ingest_helpers/__init__.py +1 -0
  179. trilogy/scripts/ingest_helpers/foreign_keys.py +123 -0
  180. trilogy/scripts/ingest_helpers/formatting.py +93 -0
  181. trilogy/scripts/ingest_helpers/typing.py +161 -0
  182. trilogy/scripts/init.py +105 -0
  183. trilogy/scripts/parallel_execution.py +748 -0
  184. trilogy/scripts/plan.py +189 -0
  185. trilogy/scripts/refresh.py +106 -0
  186. trilogy/scripts/run.py +79 -0
  187. trilogy/scripts/serve.py +202 -0
  188. trilogy/scripts/serve_helpers/__init__.py +41 -0
  189. trilogy/scripts/serve_helpers/file_discovery.py +142 -0
  190. trilogy/scripts/serve_helpers/index_generation.py +206 -0
  191. trilogy/scripts/serve_helpers/models.py +38 -0
  192. trilogy/scripts/single_execution.py +131 -0
  193. trilogy/scripts/testing.py +129 -0
  194. trilogy/scripts/trilogy.py +75 -0
  195. trilogy/std/__init__.py +0 -0
  196. trilogy/std/color.preql +3 -0
  197. trilogy/std/date.preql +13 -0
  198. trilogy/std/display.preql +18 -0
  199. trilogy/std/geography.preql +22 -0
  200. trilogy/std/metric.preql +15 -0
  201. trilogy/std/money.preql +67 -0
  202. trilogy/std/net.preql +14 -0
  203. trilogy/std/ranking.preql +7 -0
  204. trilogy/std/report.preql +5 -0
  205. trilogy/std/semantic.preql +6 -0
  206. trilogy/utility.py +34 -0
@@ -0,0 +1,135 @@
1
+ import sys
2
+ from os import environ
3
+
4
+ import networkx as nx
5
+
6
+ from trilogy.hooks.base_hook import BaseHook
7
+
8
+
9
+ class GraphHook(BaseHook):
10
+ def __init__(self):
11
+ super().__init__()
12
+ # https://github.com/python/cpython/issues/125235#issuecomment-2412948604
13
+ # Only set TCL_LIBRARY when GraphHook is instantiated (needs matplotlib/tkinter)
14
+ if sys.platform == "win32" and not environ.get("TCL_LIBRARY"):
15
+ import pathlib
16
+
17
+ # Derive TCL path from Python installation directory
18
+ python_dir = pathlib.Path(sys.executable).parent
19
+ tcl_path = python_dir / "tcl" / "tcl8.6"
20
+ if tcl_path.exists():
21
+ environ["TCL_LIBRARY"] = str(tcl_path)
22
+
23
+ def query_graph_built(
24
+ self,
25
+ graph: nx.DiGraph,
26
+ target: str | None = None,
27
+ highlight_nodes: list[str] | None = None,
28
+ remove_isolates: bool = True,
29
+ ):
30
+ from matplotlib import pyplot as plt
31
+
32
+ graph = graph.copy()
33
+ nodes = [*graph.nodes]
34
+ for node in nodes:
35
+ if "__preql_internal" in node:
36
+ graph.remove_node(node)
37
+
38
+ if remove_isolates:
39
+ graph.remove_nodes_from(list(nx.isolates(graph)))
40
+
41
+ color_map = []
42
+ highlight_nodes = highlight_nodes or []
43
+ for node in graph:
44
+ if node in highlight_nodes:
45
+ color_map.append("orange")
46
+ elif str(node).startswith("ds"):
47
+ color_map.append("blue")
48
+ else:
49
+ color_map.append("green")
50
+
51
+ pos = nx.spring_layout(graph)
52
+ kwargs = {}
53
+
54
+ if target:
55
+ edge_colors = []
56
+ descendents = nx.descendants(graph, target)
57
+ for edge in graph.edges():
58
+ if edge[0] == target:
59
+ edge_colors.append("blue")
60
+ elif edge[1] == target:
61
+ edge_colors.append("blue")
62
+ elif edge[1] in descendents:
63
+ edge_colors.append("green")
64
+ else:
65
+ edge_colors.append("black")
66
+ kwargs["edge_color"] = edge_colors
67
+
68
+ # Draw the graph without labels first
69
+ nx.draw(
70
+ graph,
71
+ pos=pos,
72
+ node_color=color_map,
73
+ connectionstyle="arc3, rad = 0.1",
74
+ with_labels=False, # Important: don't draw labels with nx.draw
75
+ **kwargs
76
+ )
77
+
78
+ # Draw labels with manual spacing
79
+ self._draw_labels_with_manual_spacing(graph, pos)
80
+
81
+ plt.show()
82
+
83
+ def _draw_labels_with_manual_spacing(self, graph, pos):
84
+ import numpy as np
85
+
86
+ pos_labels = {}
87
+ node_positions = list(pos.values())
88
+
89
+ # Calculate average distance between nodes to determine spacing
90
+ if len(node_positions) > 1:
91
+ distances = []
92
+ for i, (x1, y1) in enumerate(node_positions):
93
+ for j, (x2, y2) in enumerate(node_positions[i + 1 :], i + 1):
94
+ dist = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
95
+ distances.append(dist)
96
+
97
+ avg_distance = np.mean(distances)
98
+ min_spacing = max(
99
+ 0.1, avg_distance * 0.3
100
+ ) # Minimum spacing as fraction of average distance
101
+ else:
102
+ min_spacing = 0.1
103
+
104
+ # Simple spacing algorithm - offset labels that are too close
105
+ for i, node in enumerate(graph.nodes()):
106
+ x, y = pos[node]
107
+
108
+ # Check for nearby labels and adjust position
109
+ adjusted_x, adjusted_y = x, y
110
+ for j, other_node in enumerate(
111
+ list(graph.nodes())[:i]
112
+ ): # Only check previous nodes
113
+ other_x, other_y = pos_labels.get(other_node, pos[other_node])
114
+ distance = np.sqrt(
115
+ (adjusted_x - other_x) ** 2 + (adjusted_y - other_y) ** 2
116
+ )
117
+
118
+ if distance < min_spacing:
119
+ # Calculate offset direction
120
+ if distance > 0:
121
+ offset_x = (adjusted_x - other_x) / distance * min_spacing
122
+ offset_y = (adjusted_y - other_y) / distance * min_spacing
123
+ else:
124
+ # If nodes are at exact same position, use random offset
125
+ angle = np.random.random() * 2 * np.pi
126
+ offset_x = np.cos(angle) * min_spacing
127
+ offset_y = np.sin(angle) * min_spacing
128
+
129
+ adjusted_x = other_x + offset_x
130
+ adjusted_y = other_y + offset_y
131
+
132
+ pos_labels[node] = (adjusted_x, adjusted_y)
133
+
134
+ # Draw the labels at adjusted positions
135
+ nx.draw_networkx_labels(graph, pos=pos_labels, font_size=10)
@@ -0,0 +1,166 @@
1
+ from enum import Enum
2
+ from logging import DEBUG, StreamHandler
3
+ from typing import Union
4
+ from uuid import uuid4
5
+
6
+ from trilogy.constants import logger
7
+ from trilogy.core.models.build import BuildDatasource
8
+ from trilogy.core.models.execute import (
9
+ CTE,
10
+ QueryDatasource,
11
+ UnionCTE,
12
+ )
13
+ from trilogy.core.processing.nodes import StrategyNode
14
+ from trilogy.core.statements.author import SelectStatement
15
+ from trilogy.hooks.base_hook import BaseHook
16
+
17
+
18
+ class PrintMode(Enum):
19
+ OFF = False
20
+ BASIC = True
21
+ FULL = 3
22
+
23
+
24
+ class DebuggingHook(BaseHook):
25
+ def __init__(
26
+ self,
27
+ level=DEBUG,
28
+ max_depth: int | None = None,
29
+ process_ctes: PrintMode | bool = True,
30
+ process_nodes: PrintMode | bool = True,
31
+ process_datasources: PrintMode | bool = True,
32
+ process_other: bool = True,
33
+ ):
34
+ if not any([isinstance(x, StreamHandler) for x in logger.handlers]):
35
+ logger.addHandler(StreamHandler())
36
+ logger.setLevel(level)
37
+
38
+ self.max_depth = max_depth
39
+ self.process_ctes = PrintMode(process_ctes)
40
+ self.process_nodes = PrintMode(process_nodes)
41
+ self.process_datasources = PrintMode(process_datasources)
42
+ self.process_other = PrintMode(process_other)
43
+ self.messages: list[str] = []
44
+ self.uuid = uuid4()
45
+ from trilogy.dialect.bigquery import BigqueryDialect
46
+
47
+ self.renderer = BigqueryDialect()
48
+
49
+ def print(self, *args):
50
+ merged = " ".join([str(x) for x in args])
51
+ self.messages.append(merged)
52
+
53
+ def write(self):
54
+ with open(f"debug_{self.uuid}.log", "w") as f:
55
+ f.write("\n".join(self.messages))
56
+
57
+ def process_select_info(self, select: SelectStatement):
58
+ if self.process_datasources != PrintMode.OFF:
59
+ self.print(f"grain: {str(select.grain)}")
60
+
61
+ def process_root_datasource(self, datasource: QueryDatasource):
62
+ if self.process_datasources != PrintMode.OFF:
63
+ printed = self.print_recursive_resolved(
64
+ datasource, self.process_datasources
65
+ )
66
+ for row in printed:
67
+ self.print("".join([str(v) for v in row]))
68
+
69
+ def process_root_cte(self, cte: CTE | UnionCTE):
70
+ if self.process_ctes != PrintMode.OFF:
71
+ self.print_recursive_ctes(cte, max_depth=self.max_depth)
72
+
73
+ def process_root_strategy_node(self, node: StrategyNode):
74
+ if self.process_nodes != PrintMode.OFF:
75
+ printed = self.print_recursive_nodes(node, mode=self.process_nodes)
76
+ for row in printed:
77
+ # logger.info("".join([str(v) for v in row]))
78
+ self.print("".join([str(v) for v in row]))
79
+
80
+ def print_recursive_resolved(
81
+ self,
82
+ input: Union[QueryDatasource, BuildDatasource],
83
+ mode: PrintMode,
84
+ depth: int = 0,
85
+ ):
86
+ extra = []
87
+ if isinstance(input, QueryDatasource):
88
+ if input.joins:
89
+ extra.append("join")
90
+ if input.condition:
91
+ extra.append("filter")
92
+ if input.group_required:
93
+ extra.append("group")
94
+ output = [c.address for c in input.output_concepts[:3]]
95
+ if len(input.output_concepts) > 3:
96
+ output.append("...")
97
+ display = [
98
+ (
99
+ " " * depth,
100
+ input.__class__.__name__,
101
+ "<",
102
+ ",".join(extra),
103
+ ">",
104
+ # [c.address for c in input.input_concepts],
105
+ "->",
106
+ output,
107
+ )
108
+ ]
109
+ if isinstance(input, QueryDatasource):
110
+ for child in input.datasources:
111
+ display += self.print_recursive_resolved(
112
+ child, mode=mode, depth=depth + 1
113
+ )
114
+ return display
115
+
116
+ def print_recursive_ctes(
117
+ self, input: CTE | UnionCTE, depth: int = 0, max_depth: int | None = None
118
+ ):
119
+ if max_depth and depth > max_depth:
120
+ return
121
+ select_statement = [c.address for c in input.output_columns]
122
+ self.print(
123
+ " " * depth, input.name, "->", input.group_to_grain, "->", select_statement
124
+ )
125
+ sql = self.renderer.render_cte(input).statement
126
+ for line in sql.split("\n"):
127
+ logger.debug(" " * (depth) + line)
128
+ if isinstance(input, CTE):
129
+ for child in input.parent_ctes:
130
+ self.print_recursive_ctes(child, depth + 1)
131
+ elif isinstance(input, UnionCTE):
132
+ for child in input.parent_ctes:
133
+ for parent in child.parent_ctes:
134
+ self.print_recursive_ctes(parent, depth + 1)
135
+
136
+ def print_recursive_nodes(
137
+ self, input: StrategyNode, mode: PrintMode = PrintMode.BASIC, depth: int = 0
138
+ ):
139
+ resolved = input.resolve()
140
+ if mode == PrintMode.FULL:
141
+ display = [
142
+ [
143
+ " " * depth,
144
+ input,
145
+ "->",
146
+ resolved.grain,
147
+ "->",
148
+ [c.address for c in resolved.output_concepts],
149
+ ]
150
+ ]
151
+ elif mode == PrintMode.BASIC:
152
+ display = [
153
+ [
154
+ " " * depth,
155
+ input,
156
+ "->",
157
+ resolved.grain,
158
+ ]
159
+ ]
160
+ for child in input.parents:
161
+ display += self.print_recursive_nodes(
162
+ child,
163
+ mode=mode,
164
+ depth=depth + 1,
165
+ )
166
+ return display
File without changes
trilogy/parser.py ADDED
@@ -0,0 +1,10 @@
1
+ from typing import Optional
2
+
3
+ from trilogy.core.models.environment import Environment
4
+ from trilogy.parsing.parse_engine import parse_text
5
+
6
+
7
+ def parse(
8
+ input: str, environment: Optional[Environment] = None
9
+ ) -> tuple[Environment, list]:
10
+ return parse_text(input, environment=environment)
@@ -0,0 +1,21 @@
1
+ # Grammar Overview
2
+
3
+ ```
4
+ !start: ( block | show_statement )*
5
+ block: statement _TERMINATOR PARSE_COMMENT?
6
+ ?statement: concept
7
+ | datasource
8
+ | function
9
+ | multi_select_statement
10
+ | select_statement
11
+ | persist_statement
12
+ | rowset_derivation_statement
13
+ | import_statement
14
+ | copy_statement
15
+ | merge_statement
16
+ | rawsql_statement
17
+
18
+ _TERMINATOR: ";"i /\s*/
19
+ ```
20
+
21
+ Full grammar in trilogy.lark.
File without changes