pytrilogy 0.0.3.6__py3-none-any.whl → 0.0.3.7__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.3.6.dist-info → pytrilogy-0.0.3.7.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.3.6.dist-info → pytrilogy-0.0.3.7.dist-info}/RECORD +14 -13
- trilogy/__init__.py +1 -1
- trilogy/core/functions.py +26 -6
- trilogy/core/models/datasource.py +6 -0
- trilogy/dialect/config.py +9 -0
- trilogy/dialect/dataframe.py +42 -0
- trilogy/dialect/enums.py +12 -1
- trilogy/engine.py +11 -4
- trilogy/executor.py +10 -2
- {pytrilogy-0.0.3.6.dist-info → pytrilogy-0.0.3.7.dist-info}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.6.dist-info → pytrilogy-0.0.3.7.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.6.dist-info → pytrilogy-0.0.3.7.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.6.dist-info → pytrilogy-0.0.3.7.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
trilogy/__init__.py,sha256=
|
|
1
|
+
trilogy/__init__.py,sha256=nCVrjnf_bl_zZ7GmePdSoaRQ40QxFDkSFlafhQp8Cn8,302
|
|
2
2
|
trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
trilogy/constants.py,sha256=qZ1d0hoKPPV2HHCoFwPYTVB7b6bXjpWvXd3lE-zEhy8,1494
|
|
4
|
-
trilogy/engine.py,sha256=
|
|
5
|
-
trilogy/executor.py,sha256=
|
|
4
|
+
trilogy/engine.py,sha256=3etkm2RSVKO0IkgPKkrcs33X5gN_fIMyqMNfChcsR1E,1318
|
|
5
|
+
trilogy/executor.py,sha256=YgSCeeYVecI9526LGSLVe2apOo7Ddsttvs_nDC9yElQ,17194
|
|
6
6
|
trilogy/parser.py,sha256=o4cfk3j3yhUFoiDKq9ZX_GjBF3dKhDjXEwb63rcBkBM,293
|
|
7
7
|
trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
trilogy/utility.py,sha256=euQccZLKoYBz0LNg5tzLlvv2YHvXh9HArnYp1V3uXsM,763
|
|
@@ -14,7 +14,7 @@ trilogy/core/env_processor.py,sha256=pFsxnluKIusGKx1z7tTnfsd_xZcPy9pZDungkjkyvI0
|
|
|
14
14
|
trilogy/core/environment_helpers.py,sha256=oOpewPwMp8xOtx2ayeeyuLNUwr-cli7UanHKot5ebNY,7627
|
|
15
15
|
trilogy/core/ergonomics.py,sha256=ASLDd0RqKWrZiG3XcKHo8nyTjaB_8xfE9t4NZ1UvGpc,1639
|
|
16
16
|
trilogy/core/exceptions.py,sha256=JPYyBcit3T_pRtlHdtKSeVJkIyWUTozW2aaut25A2xI,673
|
|
17
|
-
trilogy/core/functions.py,sha256=
|
|
17
|
+
trilogy/core/functions.py,sha256=rIkZGzw9hpIkXvuqQ1qPWFJO1W_NPYc6T9t0wTZ55M0,24784
|
|
18
18
|
trilogy/core/graph_models.py,sha256=z17EoO8oky2QOuO6E2aMWoVNKEVJFhLdsQZOhC4fNLU,2079
|
|
19
19
|
trilogy/core/internal.py,sha256=iicDBlC6nM8d7e7jqzf_ZOmpUsW8yrr2AA8AqEiLx-s,1577
|
|
20
20
|
trilogy/core/optimization.py,sha256=xGO8piVsLrpqrx-Aid_Y56_5slSv4eZmlP64hCHRiEc,7957
|
|
@@ -24,7 +24,7 @@ trilogy/core/models/author.py,sha256=oRCKWhz-i1fO1LlHWiHE3l1awCHdQ3yx6FKH9n9RxRU
|
|
|
24
24
|
trilogy/core/models/build.py,sha256=kiq31T8LtUtgmT37m617Q2MlMvQTuAxJzwb6947EiWU,56127
|
|
25
25
|
trilogy/core/models/build_environment.py,sha256=8UggvlPU708GZWYPJMc_ou2r7M3TY2g69eqGvz03YX0,5528
|
|
26
26
|
trilogy/core/models/core.py,sha256=yie1uuq62uOQ5fjob9NMJbdvQPrCErXUT7JTCuYRyjI,9697
|
|
27
|
-
trilogy/core/models/datasource.py,sha256=
|
|
27
|
+
trilogy/core/models/datasource.py,sha256=6RjJUd2u4nYmEwFBpJlM9LbHVYDv8iHJxqiBMZqUrwI,9422
|
|
28
28
|
trilogy/core/models/environment.py,sha256=h06y1Dv7naw2GuFFAAyoFZmicG7a7Lu-dRoYPVfrOGo,25967
|
|
29
29
|
trilogy/core/models/execute.py,sha256=ABylFQgtavjjCfFkEsFdUwfMB4UBQLHjdzQ9E67QlAE,33521
|
|
30
30
|
trilogy/core/optimizations/__init__.py,sha256=EBanqTXEzf1ZEYjAneIWoIcxtMDite5-n2dQ5xcfUtg,356
|
|
@@ -71,9 +71,10 @@ trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
71
71
|
trilogy/dialect/base.py,sha256=u00kIIl98as1QzcduiiyyoBzxRGVeBxfeO5hWlRCAJU,40222
|
|
72
72
|
trilogy/dialect/bigquery.py,sha256=mKC3zoEU232h9RtIXJjqiZ72lWH8a6S28p6wAZKrAfg,2952
|
|
73
73
|
trilogy/dialect/common.py,sha256=cbTo_vamdp8pj9spSjGSH-bSZpy4FpNJ12k5vMvyT2Y,3942
|
|
74
|
-
trilogy/dialect/config.py,sha256=
|
|
74
|
+
trilogy/dialect/config.py,sha256=e-ZDVh7Z648JYz85JwSobTyo2cTi4lYGFMglZzB7atM,3184
|
|
75
|
+
trilogy/dialect/dataframe.py,sha256=ei5y91XyZHI3ydUbdQ2sInnw2qHGtgb21DNX6qff0xw,1419
|
|
75
76
|
trilogy/dialect/duckdb.py,sha256=2tH_OetgLJoKf_f4bdeeB0ozGC8f0h_xQ271I8qD-Oo,3690
|
|
76
|
-
trilogy/dialect/enums.py,sha256=
|
|
77
|
+
trilogy/dialect/enums.py,sha256=1KDgds_DC31hGxZzNI_TIggxXF7m9rIjn9KLgNf5WQU,4425
|
|
77
78
|
trilogy/dialect/postgres.py,sha256=VH4EB4myjIeZTHeFU6vK00GxY9c53rCBjg2mLbdaCEE,3254
|
|
78
79
|
trilogy/dialect/presto.py,sha256=bAxaDcLL21fivPg7hmBd3HJmd0yYJdPdwNgNA5ga7DE,3391
|
|
79
80
|
trilogy/dialect/snowflake.py,sha256=wmao9p26jX5yIX5SC8sRAZTXkPGTvq6ixO693QTfhz8,2989
|
|
@@ -93,9 +94,9 @@ trilogy/parsing/render.py,sha256=o_XuQWhcwx1lD9eGVqkqZEwkmQK0HdmWWokGBtdeH4I,178
|
|
|
93
94
|
trilogy/parsing/trilogy.lark,sha256=EazfEvYPuvkPkNjUnVzFi0uD9baavugbSI8CyfawShk,12573
|
|
94
95
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
95
96
|
trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
|
|
96
|
-
pytrilogy-0.0.3.
|
|
97
|
-
pytrilogy-0.0.3.
|
|
98
|
-
pytrilogy-0.0.3.
|
|
99
|
-
pytrilogy-0.0.3.
|
|
100
|
-
pytrilogy-0.0.3.
|
|
101
|
-
pytrilogy-0.0.3.
|
|
97
|
+
pytrilogy-0.0.3.7.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
98
|
+
pytrilogy-0.0.3.7.dist-info/METADATA,sha256=wvr0oUtX0As37OC9ljg5XnV7rblzMNvUppA4il2PtPI,8983
|
|
99
|
+
pytrilogy-0.0.3.7.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
100
|
+
pytrilogy-0.0.3.7.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
|
|
101
|
+
pytrilogy-0.0.3.7.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
102
|
+
pytrilogy-0.0.3.7.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/core/functions.py
CHANGED
|
@@ -503,32 +503,52 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
503
503
|
arg_count=1,
|
|
504
504
|
),
|
|
505
505
|
FunctionType.ADD: FunctionConfig(
|
|
506
|
-
valid_inputs={
|
|
506
|
+
valid_inputs={
|
|
507
|
+
DataType.INTEGER,
|
|
508
|
+
DataType.FLOAT,
|
|
509
|
+
DataType.NUMBER,
|
|
510
|
+
DataType.NUMERIC,
|
|
511
|
+
},
|
|
507
512
|
output_purpose=Purpose.PROPERTY,
|
|
508
513
|
output_type=DataType.INTEGER,
|
|
509
514
|
arg_count=InfiniteFunctionArgs,
|
|
510
515
|
),
|
|
511
516
|
FunctionType.SUBTRACT: FunctionConfig(
|
|
512
|
-
valid_inputs={
|
|
517
|
+
valid_inputs={
|
|
518
|
+
DataType.INTEGER,
|
|
519
|
+
DataType.FLOAT,
|
|
520
|
+
DataType.NUMBER,
|
|
521
|
+
DataType.NUMERIC,
|
|
522
|
+
},
|
|
513
523
|
output_purpose=Purpose.PROPERTY,
|
|
514
524
|
output_type=DataType.INTEGER,
|
|
515
525
|
arg_count=InfiniteFunctionArgs,
|
|
516
526
|
),
|
|
517
527
|
FunctionType.MULTIPLY: FunctionConfig(
|
|
518
|
-
valid_inputs={
|
|
528
|
+
valid_inputs={
|
|
529
|
+
DataType.INTEGER,
|
|
530
|
+
DataType.FLOAT,
|
|
531
|
+
DataType.NUMBER,
|
|
532
|
+
DataType.NUMERIC,
|
|
533
|
+
},
|
|
519
534
|
output_purpose=Purpose.PROPERTY,
|
|
520
535
|
output_type=DataType.INTEGER,
|
|
521
536
|
arg_count=InfiniteFunctionArgs,
|
|
522
537
|
),
|
|
523
538
|
FunctionType.DIVIDE: FunctionConfig(
|
|
524
|
-
valid_inputs={
|
|
539
|
+
valid_inputs={
|
|
540
|
+
DataType.INTEGER,
|
|
541
|
+
DataType.FLOAT,
|
|
542
|
+
DataType.NUMBER,
|
|
543
|
+
DataType.NUMERIC,
|
|
544
|
+
},
|
|
525
545
|
output_purpose=Purpose.PROPERTY,
|
|
526
546
|
output_type=DataType.INTEGER,
|
|
527
547
|
arg_count=InfiniteFunctionArgs,
|
|
528
548
|
),
|
|
529
549
|
FunctionType.MOD: FunctionConfig(
|
|
530
550
|
valid_inputs=[
|
|
531
|
-
{DataType.INTEGER, DataType.FLOAT, DataType.NUMBER},
|
|
551
|
+
{DataType.INTEGER, DataType.FLOAT, DataType.NUMBER, DataType.NUMERIC},
|
|
532
552
|
{DataType.INTEGER},
|
|
533
553
|
],
|
|
534
554
|
output_purpose=Purpose.PROPERTY,
|
|
@@ -537,7 +557,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
537
557
|
),
|
|
538
558
|
FunctionType.ROUND: FunctionConfig(
|
|
539
559
|
valid_inputs=[
|
|
540
|
-
{DataType.INTEGER, DataType.FLOAT, DataType.NUMBER},
|
|
560
|
+
{DataType.INTEGER, DataType.FLOAT, DataType.NUMBER, DataType.NUMERIC},
|
|
541
561
|
{DataType.INTEGER},
|
|
542
562
|
],
|
|
543
563
|
output_purpose=Purpose.PROPERTY,
|
|
@@ -117,6 +117,12 @@ class Datasource(HasUUID, Namespaced, BaseModel):
|
|
|
117
117
|
where: Optional[WhereClause] = None
|
|
118
118
|
non_partial_for: Optional[WhereClause] = None
|
|
119
119
|
|
|
120
|
+
@property
|
|
121
|
+
def safe_address(self) -> str:
|
|
122
|
+
if isinstance(self.address, Address):
|
|
123
|
+
return self.address.location
|
|
124
|
+
return self.address
|
|
125
|
+
|
|
120
126
|
def __eq__(self, other):
|
|
121
127
|
if not isinstance(other, Datasource):
|
|
122
128
|
return False
|
trilogy/dialect/config.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
from pandas import DataFrame
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
class DialectConfig:
|
|
2
5
|
def __init__(self):
|
|
3
6
|
pass
|
|
@@ -104,3 +107,9 @@ class TrinoConfig(PrestoConfig):
|
|
|
104
107
|
if self.schema:
|
|
105
108
|
return f"trino://{self.username}:{self.password}@{self.host}:{self.port}/{self.catalog}/{self.schema}"
|
|
106
109
|
return f"trino://{self.username}:{self.password}@{self.host}:{self.port}/{self.catalog}"
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class DataFrameConfig(DuckDBConfig):
|
|
113
|
+
def __init__(self, dataframes: dict[str, DataFrame]):
|
|
114
|
+
super().__init__()
|
|
115
|
+
self.dataframes = dataframes
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from pandas import DataFrame
|
|
4
|
+
from sqlalchemy import text
|
|
5
|
+
|
|
6
|
+
from trilogy.core.models.environment import Environment
|
|
7
|
+
from trilogy.dialect.duckdb import DuckDBDialect
|
|
8
|
+
from trilogy.engine import ExecutionEngine
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DataframeDialect(DuckDBDialect):
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DataframeConnectionWrapper(ExecutionEngine):
|
|
16
|
+
def __init__(self, engine: ExecutionEngine, dataframes: dict[str, DataFrame]):
|
|
17
|
+
self.engine = engine
|
|
18
|
+
self.dataframes = dataframes
|
|
19
|
+
self.connection = None
|
|
20
|
+
|
|
21
|
+
def setup(self, env: Environment, connection):
|
|
22
|
+
self._register_dataframes(env, connection)
|
|
23
|
+
|
|
24
|
+
def _register_dataframes(self, env: Environment, connection):
|
|
25
|
+
for ds in env.datasources.values():
|
|
26
|
+
if ds.safe_address in self.dataframes:
|
|
27
|
+
connection.execute(
|
|
28
|
+
text("register(:name, :df)"),
|
|
29
|
+
{"name": ds.safe_address, "df": self.dataframes[ds.safe_address]},
|
|
30
|
+
)
|
|
31
|
+
else:
|
|
32
|
+
raise ValueError(
|
|
33
|
+
f"Dataframe {ds.safe_address} not found in dataframes on connection config, have {self.dataframes.keys()}"
|
|
34
|
+
)
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
def add_dataframe(self, name: str, df: DataFrame, connection, env: Environment):
|
|
38
|
+
self.dataframes[name] = df
|
|
39
|
+
self._register_dataframes(env, connection)
|
|
40
|
+
|
|
41
|
+
def connect(self) -> Any:
|
|
42
|
+
return self.engine.connect()
|
trilogy/dialect/enums.py
CHANGED
|
@@ -16,7 +16,7 @@ def default_factory(conf: DialectConfig, config_type):
|
|
|
16
16
|
|
|
17
17
|
if not isinstance(conf, config_type):
|
|
18
18
|
raise TypeError(
|
|
19
|
-
f"Invalid dialect configuration for type {type(config_type).__name__}"
|
|
19
|
+
f"Invalid dialect configuration for type {type(config_type).__name__}, is {type(conf)}"
|
|
20
20
|
)
|
|
21
21
|
if conf.connect_args:
|
|
22
22
|
return create_engine(
|
|
@@ -33,6 +33,7 @@ class Dialects(Enum):
|
|
|
33
33
|
TRINO = "trino"
|
|
34
34
|
POSTGRES = "postgres"
|
|
35
35
|
SNOWFLAKE = "snowflake"
|
|
36
|
+
DATAFRAME = "dataframe"
|
|
36
37
|
|
|
37
38
|
@classmethod
|
|
38
39
|
def _missing_(cls, value):
|
|
@@ -88,6 +89,16 @@ class Dialects(Enum):
|
|
|
88
89
|
from trilogy.dialect.config import TrinoConfig
|
|
89
90
|
|
|
90
91
|
return _engine_factory(conf, TrinoConfig)
|
|
92
|
+
elif self == Dialects.DATAFRAME:
|
|
93
|
+
from trilogy.dialect.config import DataFrameConfig
|
|
94
|
+
from trilogy.dialect.dataframe import DataframeConnectionWrapper
|
|
95
|
+
|
|
96
|
+
if not conf:
|
|
97
|
+
conf = DataFrameConfig(dataframes={})
|
|
98
|
+
|
|
99
|
+
base = _engine_factory(conf, DataFrameConfig)
|
|
100
|
+
|
|
101
|
+
return DataframeConnectionWrapper(base, dataframes=conf.dataframes)
|
|
91
102
|
else:
|
|
92
103
|
raise ValueError(
|
|
93
104
|
f"Unsupported dialect {self} for default engine creation; create one explicitly."
|
trilogy/engine.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
from typing import Protocol
|
|
1
|
+
from typing import Any, Protocol
|
|
2
2
|
|
|
3
3
|
from sqlalchemy.engine import Connection, CursorResult, Engine
|
|
4
4
|
|
|
5
|
+
from trilogy.core.models.environment import Environment
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
class EngineResult(Protocol):
|
|
7
9
|
pass
|
|
@@ -13,7 +15,7 @@ class EngineResult(Protocol):
|
|
|
13
15
|
class EngineConnection(Protocol):
|
|
14
16
|
pass
|
|
15
17
|
|
|
16
|
-
def execute(self, statement: str) -> EngineResult:
|
|
18
|
+
def execute(self, statement: str, parameters: Any | None = None) -> EngineResult:
|
|
17
19
|
pass
|
|
18
20
|
|
|
19
21
|
|
|
@@ -23,6 +25,9 @@ class ExecutionEngine(Protocol):
|
|
|
23
25
|
def connect(self) -> EngineConnection:
|
|
24
26
|
pass
|
|
25
27
|
|
|
28
|
+
def setup(self, env: Environment, connection):
|
|
29
|
+
pass
|
|
30
|
+
|
|
26
31
|
|
|
27
32
|
### Begin default SQLAlchemy implementation
|
|
28
33
|
class SqlAlchemyResult(EngineResult):
|
|
@@ -37,8 +42,10 @@ class SqlAlchemyConnection(EngineConnection):
|
|
|
37
42
|
def __init__(self, connection: Connection):
|
|
38
43
|
self.connection = connection
|
|
39
44
|
|
|
40
|
-
def execute(
|
|
41
|
-
|
|
45
|
+
def execute(
|
|
46
|
+
self, statement: str, parameters: Any | None = None
|
|
47
|
+
) -> SqlAlchemyResult:
|
|
48
|
+
return SqlAlchemyResult(self.connection.execute(statement, parameters))
|
|
42
49
|
|
|
43
50
|
|
|
44
51
|
class SqlAlchemyEngine(ExecutionEngine):
|
trilogy/executor.py
CHANGED
|
@@ -4,7 +4,7 @@ from pathlib import Path
|
|
|
4
4
|
from typing import Any, Generator, List, Optional, Protocol
|
|
5
5
|
|
|
6
6
|
from sqlalchemy import text
|
|
7
|
-
from sqlalchemy.engine import CursorResult
|
|
7
|
+
from sqlalchemy.engine import CursorResult
|
|
8
8
|
|
|
9
9
|
from trilogy.constants import logger
|
|
10
10
|
from trilogy.core.enums import FunctionType, Granularity, IOType
|
|
@@ -33,6 +33,7 @@ from trilogy.core.statements.execute import (
|
|
|
33
33
|
)
|
|
34
34
|
from trilogy.dialect.base import BaseDialect
|
|
35
35
|
from trilogy.dialect.enums import Dialects
|
|
36
|
+
from trilogy.engine import ExecutionEngine
|
|
36
37
|
from trilogy.hooks.base_hook import BaseHook
|
|
37
38
|
from trilogy.parser import parse_text
|
|
38
39
|
|
|
@@ -71,7 +72,7 @@ class Executor(object):
|
|
|
71
72
|
def __init__(
|
|
72
73
|
self,
|
|
73
74
|
dialect: Dialects,
|
|
74
|
-
engine:
|
|
75
|
+
engine: ExecutionEngine,
|
|
75
76
|
environment: Optional[Environment] = None,
|
|
76
77
|
hooks: List[BaseHook] | None = None,
|
|
77
78
|
):
|
|
@@ -109,9 +110,16 @@ class Executor(object):
|
|
|
109
110
|
from trilogy.dialect.snowflake import SnowflakeDialect
|
|
110
111
|
|
|
111
112
|
self.generator = SnowflakeDialect()
|
|
113
|
+
elif self.dialect == Dialects.DATAFRAME:
|
|
114
|
+
from trilogy.dialect.dataframe import DataframeDialect
|
|
115
|
+
|
|
116
|
+
self.generator = DataframeDialect()
|
|
112
117
|
else:
|
|
113
118
|
raise ValueError(f"Unsupported dialect {self.dialect}")
|
|
114
119
|
self.connection = self.engine.connect()
|
|
120
|
+
# TODO: make generic
|
|
121
|
+
if self.dialect == Dialects.DATAFRAME:
|
|
122
|
+
self.engine.setup(self.environment, self.connection)
|
|
115
123
|
|
|
116
124
|
def execute_statement(self, statement) -> Optional[CursorResult]:
|
|
117
125
|
if not isinstance(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|