pytrilogy 0.0.2.36__tar.gz → 0.0.2.37__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.

Files changed (110) hide show
  1. {pytrilogy-0.0.2.36/pytrilogy.egg-info → pytrilogy-0.0.2.37}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_select.py +1 -1
  4. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/__init__.py +1 -1
  5. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/constants.py +8 -0
  6. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/models.py +17 -16
  7. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/dialect/base.py +8 -1
  8. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/executor.py +47 -8
  9. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/parsing/parse_engine.py +2 -1
  10. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/parsing/trilogy.lark +1 -1
  11. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/LICENSE.md +0 -0
  12. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/README.md +0 -0
  13. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/pyproject.toml +0 -0
  14. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/pytrilogy.egg-info/SOURCES.txt +0 -0
  15. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/pytrilogy.egg-info/dependency_links.txt +0 -0
  16. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/pytrilogy.egg-info/entry_points.txt +0 -0
  17. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/pytrilogy.egg-info/requires.txt +0 -0
  18. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/pytrilogy.egg-info/top_level.txt +0 -0
  19. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/setup.cfg +0 -0
  20. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/setup.py +0 -0
  21. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_datatypes.py +0 -0
  22. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_declarations.py +0 -0
  23. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_derived_concepts.py +0 -0
  24. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_discovery_nodes.py +0 -0
  25. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_enums.py +0 -0
  26. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_environment.py +0 -0
  27. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_executor.py +0 -0
  28. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_functions.py +0 -0
  29. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_imports.py +0 -0
  30. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_metadata.py +0 -0
  31. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_models.py +0 -0
  32. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_multi_join_assignments.py +0 -0
  33. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_parse_engine.py +0 -0
  34. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_parsing.py +0 -0
  35. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_partial_handling.py +0 -0
  36. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_query_processing.py +0 -0
  37. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_show.py +0 -0
  38. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_statements.py +0 -0
  39. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_undefined_concept.py +0 -0
  40. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/tests/test_where_clause.py +0 -0
  41. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/compiler.py +0 -0
  42. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/__init__.py +0 -0
  43. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/constants.py +0 -0
  44. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/enums.py +0 -0
  45. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/env_processor.py +0 -0
  46. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/environment_helpers.py +0 -0
  47. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/ergonomics.py +0 -0
  48. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/exceptions.py +0 -0
  49. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/functions.py +0 -0
  50. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/graph_models.py +0 -0
  51. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/internal.py +0 -0
  52. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/optimization.py +0 -0
  53. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/optimizations/__init__.py +0 -0
  54. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/optimizations/base_optimization.py +0 -0
  55. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/optimizations/inline_constant.py +0 -0
  56. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/optimizations/inline_datasource.py +0 -0
  57. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  58. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/__init__.py +0 -0
  59. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  60. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/graph_utils.py +0 -0
  61. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/node_generators/__init__.py +0 -0
  62. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  63. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/node_generators/common.py +0 -0
  64. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  65. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/node_generators/group_node.py +0 -0
  66. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  67. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  68. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  69. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  70. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  71. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/node_generators/select_node.py +0 -0
  72. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  73. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/node_generators/window_node.py +0 -0
  74. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/nodes/__init__.py +0 -0
  75. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/nodes/base_node.py +0 -0
  76. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/nodes/filter_node.py +0 -0
  77. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/nodes/group_node.py +0 -0
  78. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/nodes/merge_node.py +0 -0
  79. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  80. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  81. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/nodes/window_node.py +0 -0
  82. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/processing/utility.py +0 -0
  83. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/core/query_processor.py +0 -0
  84. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/dialect/__init__.py +0 -0
  85. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/dialect/bigquery.py +0 -0
  86. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/dialect/common.py +0 -0
  87. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/dialect/config.py +0 -0
  88. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/dialect/duckdb.py +0 -0
  89. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/dialect/enums.py +0 -0
  90. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/dialect/postgres.py +0 -0
  91. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/dialect/presto.py +0 -0
  92. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/dialect/snowflake.py +0 -0
  93. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/dialect/sql_server.py +0 -0
  94. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/engine.py +0 -0
  95. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/hooks/__init__.py +0 -0
  96. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/hooks/base_hook.py +0 -0
  97. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/hooks/graph_hook.py +0 -0
  98. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/hooks/query_debugger.py +0 -0
  99. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/metadata/__init__.py +0 -0
  100. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/parser.py +0 -0
  101. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/parsing/__init__.py +0 -0
  102. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/parsing/common.py +0 -0
  103. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/parsing/config.py +0 -0
  104. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/parsing/exceptions.py +0 -0
  105. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/parsing/helpers.py +0 -0
  106. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/parsing/render.py +0 -0
  107. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/py.typed +0 -0
  108. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/scripts/__init__.py +0 -0
  109. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/scripts/trilogy.py +0 -0
  110. {pytrilogy-0.0.2.36 → pytrilogy-0.0.2.37}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.36
3
+ Version: 0.0.2.37
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.2.36
3
+ Version: 0.0.2.37
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -126,7 +126,7 @@ def test_modifiers():
126
126
  generator = BigqueryDialect()
127
127
 
128
128
  text = generator.compile_statement(query)
129
- assert "2 = 2" in text
129
+ assert ":b = 2" in text
130
130
  assert "as `b`" not in text
131
131
 
132
132
 
@@ -4,6 +4,6 @@ from trilogy.executor import Executor
4
4
  from trilogy.parser import parse
5
5
  from trilogy.constants import CONFIG
6
6
 
7
- __version__ = "0.0.2.36"
7
+ __version__ = "0.0.2.37"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -39,6 +39,13 @@ class Comments:
39
39
  partial: bool = True
40
40
 
41
41
 
42
+ @dataclass
43
+ class Rendering:
44
+ """Control how the SQL is rendered"""
45
+
46
+ parameters: bool = True
47
+
48
+
42
49
  # TODO: support loading from environments
43
50
  @dataclass
44
51
  class Config:
@@ -48,6 +55,7 @@ class Config:
48
55
  validate_missing: bool = True
49
56
  comments: Comments = field(default_factory=Comments)
50
57
  optimizations: Optimizations = field(default_factory=Optimizations)
58
+ rendering: Rendering = field(default_factory=Rendering)
51
59
 
52
60
  @property
53
61
  def show_comments(self) -> bool:
@@ -3381,6 +3381,21 @@ class Environment(BaseModel):
3381
3381
  materialized_concepts: List[Concept] = Field(default_factory=list)
3382
3382
  alias_origin_lookup: Dict[str, Concept] = Field(default_factory=dict)
3383
3383
 
3384
+ def __init__(self, **data):
3385
+ super().__init__(**data)
3386
+ self.concepts["_env_working_path"] = Concept(
3387
+ name="_env_working_path",
3388
+ namespace=self.namespace,
3389
+ lineage=Function(
3390
+ operator=FunctionType.CONSTANT,
3391
+ arguments=[str(self.working_path)],
3392
+ output_datatype=DataType.STRING,
3393
+ output_purpose=Purpose.CONSTANT,
3394
+ ),
3395
+ datatype=DataType.STRING,
3396
+ purpose=Purpose.CONSTANT,
3397
+ )
3398
+
3384
3399
  @classmethod
3385
3400
  def from_file(cls, path: str | Path) -> "Environment":
3386
3401
  with open(path, "r") as f:
@@ -3554,16 +3569,7 @@ class Environment(BaseModel):
3554
3569
  target = target.with_suffix(".preql")
3555
3570
  else:
3556
3571
  target = path
3557
- if alias in self.imports:
3558
- imports = self.imports[alias]
3559
- for x in imports:
3560
- if x.path == target:
3561
- return imports
3562
- if env:
3563
- self.imports[alias].append(
3564
- ImportStatement(alias=alias, path=target, environment=env)
3565
- )
3566
- else:
3572
+ if not env:
3567
3573
  parse_address = gen_cache_lookup(str(target), alias, str(self.working_path))
3568
3574
  try:
3569
3575
  with open(target, "r", encoding="utf-8") as f:
@@ -3587,13 +3593,8 @@ class Environment(BaseModel):
3587
3593
  f"Unable to import file {target.parent}, parsing error: {e}"
3588
3594
  )
3589
3595
  env = nparser.environment
3590
- for _, concept in env.concepts.items():
3591
- self.add_concept(concept.with_namespace(alias))
3592
-
3593
- for _, datasource in env.datasources.items():
3594
- self.add_datasource(datasource.with_namespace(alias))
3595
3596
  imps = ImportStatement(alias=alias, path=target, environment=env)
3596
- self.imports[alias].append(imps)
3597
+ self.add_import(alias, source=env, imp_stm=imps)
3597
3598
  return imps
3598
3599
 
3599
3600
  def parse(
@@ -337,6 +337,13 @@ class BaseDialect:
337
337
  " target grain"
338
338
  )
339
339
  rval = f"{self.FUNCTION_GRAIN_MATCH_MAP[c.lineage.function.operator](args)}"
340
+ elif (
341
+ isinstance(c.lineage, Function)
342
+ and c.lineage.operator == FunctionType.CONSTANT
343
+ and CONFIG.rendering.parameters is True
344
+ and c.datatype.data_type != DataType.MAP
345
+ ):
346
+ rval = f":{c.safe_address}"
340
347
  else:
341
348
  args = [
342
349
  self.render_expr(
@@ -541,7 +548,7 @@ class BaseDialect:
541
548
  else:
542
549
  raise ValueError(f"Unable to render type {type(e)} {e}")
543
550
 
544
- def render_cte(self, cte: CTE, auto_sort: bool = True):
551
+ def render_cte(self, cte: CTE, auto_sort: bool = True) -> CompiledCTE:
545
552
  if self.UNNEST_MODE in (
546
553
  UnnestMode.CROSS_APPLY,
547
554
  UnnestMode.CROSS_JOIN,
@@ -22,10 +22,14 @@ from trilogy.core.models import (
22
22
  CopyStatement,
23
23
  ImportStatement,
24
24
  MergeStatementV2,
25
+ Function,
26
+ FunctionType,
27
+ MapWrapper,
28
+ ListWrapper,
25
29
  )
26
30
  from trilogy.dialect.base import BaseDialect
27
31
  from trilogy.dialect.enums import Dialects
28
- from trilogy.core.enums import IOType
32
+ from trilogy.core.enums import IOType, Granularity
29
33
  from trilogy.parser import parse_text
30
34
  from trilogy.hooks.base_hook import BaseHook
31
35
  from pathlib import Path
@@ -185,7 +189,6 @@ class Executor(object):
185
189
 
186
190
  @execute_query.register
187
191
  def _(self, query: ImportStatement) -> CursorResult:
188
- self.environment.add_file_import(query.path, query.alias)
189
192
  return MockResult(
190
193
  [
191
194
  {
@@ -219,8 +222,7 @@ class Executor(object):
219
222
  @execute_query.register
220
223
  def _(self, query: ProcessedQuery) -> CursorResult:
221
224
  sql = self.generator.compile_statement(query)
222
- # connection = self.engine.connect()
223
- output = self.connection.execute(text(sql))
225
+ output = self.execute_raw_sql(sql)
224
226
  return output
225
227
 
226
228
  @execute_query.register
@@ -228,14 +230,14 @@ class Executor(object):
228
230
 
229
231
  sql = self.generator.compile_statement(query)
230
232
 
231
- output = self.connection.execute(text(sql))
233
+ output = self.execute_raw_sql(sql)
232
234
  self.environment.add_datasource(query.datasource)
233
235
  return output
234
236
 
235
237
  @execute_query.register
236
238
  def _(self, query: ProcessedCopyStatement) -> CursorResult:
237
239
  sql = self.generator.compile_statement(query)
238
- output: CursorResult = self.connection.execute(text(sql))
240
+ output: CursorResult = self.execute_raw_sql(sql)
239
241
  if query.target_type == IOType.CSV:
240
242
  import csv
241
243
 
@@ -244,7 +246,7 @@ class Executor(object):
244
246
  outcsv.writerow(output.keys())
245
247
  outcsv.writerows(output)
246
248
  else:
247
- raise NotImplementedError(f"Unsupported IOType {query.target_type}")
249
+ raise NotImplementedError(f"Unsupported IO Type {query.target_type}")
248
250
  # now return the query we ran through IO
249
251
  # TODO: instead return how many rows were written?
250
252
  return generate_result_set(
@@ -370,13 +372,50 @@ class Executor(object):
370
372
  if persist and isinstance(x, ProcessedQueryPersist):
371
373
  self.environment.add_datasource(x.datasource)
372
374
 
375
+ def _hydrate_param(self, param: str) -> Any:
376
+ matched = [
377
+ v
378
+ for v in self.environment.concepts.values()
379
+ if v.safe_address == param or v.address == param
380
+ ]
381
+ if not matched:
382
+ raise SyntaxError(f"No concept found for parameter {param}")
383
+
384
+ concept: Concept = matched.pop()
385
+ if not concept.granularity == Granularity.SINGLE_ROW:
386
+ raise SyntaxError(f"Cannot bind non-singleton concept {concept.address}")
387
+ if (
388
+ isinstance(concept.lineage, Function)
389
+ and concept.lineage.operator == FunctionType.CONSTANT
390
+ ):
391
+ rval = concept.lineage.arguments[0]
392
+ if isinstance(rval, ListWrapper):
393
+ return [x for x in rval]
394
+ if isinstance(rval, MapWrapper):
395
+ return {k: v for k, v in rval.items()}
396
+ return rval
397
+ else:
398
+ results = self.execute_query(f"select {concept.name} limit 1;").fetchone()
399
+ if not results:
400
+ return None
401
+ return results[0]
402
+
373
403
  def execute_raw_sql(
374
404
  self, command: str, variables: dict | None = None
375
405
  ) -> CursorResult:
376
406
  """Run a command against the raw underlying
377
407
  execution engine"""
408
+ final_params = None
409
+ q = text(command)
378
410
  if variables:
379
- return self.connection.execute(text(command), variables)
411
+ final_params = variables
412
+ else:
413
+ params = q.compile().params
414
+ if params:
415
+ final_params = {x: self._hydrate_param(x) for x in params}
416
+
417
+ if final_params:
418
+ return self.connection.execute(text(command), final_params)
380
419
  return self.connection.execute(
381
420
  text(command),
382
421
  )
@@ -804,7 +804,8 @@ class ParseToObjects(Transformer):
804
804
 
805
805
  @v_args(meta=True)
806
806
  def rawsql_statement(self, meta: Meta, args) -> RawSQLStatement:
807
- return RawSQLStatement(meta=Metadata(line_number=meta.line), text=args[0])
807
+ statement = RawSQLStatement(meta=Metadata(line_number=meta.line), text=args[0])
808
+ return statement
808
809
 
809
810
  def COPY_TYPE(self, args) -> IOType:
810
811
  return IOType(args.value)
@@ -302,7 +302,7 @@
302
302
  SINGLE_STRING_CHARS: /(?:(?!\${)([^'\\]|\\.))+/+ // any character except '
303
303
  _single_quote: "'" ( SINGLE_STRING_CHARS )* "'"
304
304
  _double_quote: "\"" ( DOUBLE_STRING_CHARS )* "\""
305
- string_lit: _single_quote | _double_quote
305
+ string_lit: _single_quote | _double_quote | MULTILINE_STRING
306
306
 
307
307
  MINUS: "-"
308
308
 
File without changes
File without changes
File without changes
File without changes