fakesnow 0.9.12__py3-none-any.whl → 0.9.14__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.
- fakesnow/__init__.py +6 -1
- fakesnow/fakes.py +9 -3
- fakesnow/transforms.py +39 -25
- {fakesnow-0.9.12.dist-info → fakesnow-0.9.14.dist-info}/METADATA +3 -3
- {fakesnow-0.9.12.dist-info → fakesnow-0.9.14.dist-info}/RECORD +9 -9
- {fakesnow-0.9.12.dist-info → fakesnow-0.9.14.dist-info}/LICENSE +0 -0
- {fakesnow-0.9.12.dist-info → fakesnow-0.9.14.dist-info}/WHEEL +0 -0
- {fakesnow-0.9.12.dist-info → fakesnow-0.9.14.dist-info}/entry_points.txt +0 -0
- {fakesnow-0.9.12.dist-info → fakesnow-0.9.14.dist-info}/top_level.txt +0 -0
fakesnow/__init__.py
CHANGED
@@ -21,6 +21,7 @@ def patch(
|
|
21
21
|
create_database_on_connect: bool = True,
|
22
22
|
create_schema_on_connect: bool = True,
|
23
23
|
db_path: str | os.PathLike | None = None,
|
24
|
+
nop_regexes: list[str] | None = None,
|
24
25
|
) -> Iterator[None]:
|
25
26
|
"""Patch snowflake targets with fakes.
|
26
27
|
|
@@ -36,8 +37,11 @@ def patch(
|
|
36
37
|
|
37
38
|
create_database_on_connect (bool, optional): Create database if provided in connection. Defaults to True.
|
38
39
|
create_schema_on_connect (bool, optional): Create schema if provided in connection. Defaults to True.
|
39
|
-
db_path (str | os.PathLike | None, optional):
|
40
|
+
db_path (str | os.PathLike | None, optional): Use existing database files from this path
|
40
41
|
or create them here if they don't already exist. If None databases are in-memory. Defaults to None.
|
42
|
+
nop_regexes (list[str] | None, optional): SQL statements matching these regexes (case-insensitive) will return
|
43
|
+
the success response without being run. Useful to skip over SQL commands that aren't implemented yet.
|
44
|
+
Defaults to None.
|
41
45
|
|
42
46
|
Yields:
|
43
47
|
Iterator[None]: None.
|
@@ -57,6 +61,7 @@ def patch(
|
|
57
61
|
create_database=create_database_on_connect,
|
58
62
|
create_schema=create_schema_on_connect,
|
59
63
|
db_path=db_path,
|
64
|
+
nop_regexes=nop_regexes,
|
60
65
|
**kwargs,
|
61
66
|
),
|
62
67
|
snowflake.connector.pandas_tools.write_pandas: fakes.write_pandas,
|
fakesnow/fakes.py
CHANGED
@@ -135,8 +135,11 @@ class FakeSnowflakeCursor:
|
|
135
135
|
print(f"{command};{params=}" if params else f"{command};", file=sys.stderr)
|
136
136
|
|
137
137
|
command, params = self._rewrite_with_params(command, params)
|
138
|
-
|
139
|
-
|
138
|
+
if self._conn.nop_regexes and any(re.match(p, command, re.IGNORECASE) for p in self._conn.nop_regexes):
|
139
|
+
transformed = transforms.SUCCESS_NOP
|
140
|
+
else:
|
141
|
+
expression = parse_one(command, read="snowflake")
|
142
|
+
transformed = self._transform(expression)
|
140
143
|
return self._execute(transformed, params)
|
141
144
|
except snowflake.connector.errors.ProgrammingError as e:
|
142
145
|
self._sqlstate = e.sqlstate
|
@@ -172,7 +175,7 @@ class FakeSnowflakeCursor:
|
|
172
175
|
.transform(transforms.to_timestamp_ntz)
|
173
176
|
.transform(transforms.to_timestamp)
|
174
177
|
.transform(transforms.object_construct)
|
175
|
-
.transform(transforms.
|
178
|
+
.transform(transforms.timestamp_ntz)
|
176
179
|
.transform(transforms.float_to_double)
|
177
180
|
.transform(transforms.integer_precision)
|
178
181
|
.transform(transforms.extract_text_length)
|
@@ -195,6 +198,7 @@ class FakeSnowflakeCursor:
|
|
195
198
|
.transform(transforms.create_user)
|
196
199
|
.transform(transforms.sha256)
|
197
200
|
.transform(transforms.create_clone)
|
201
|
+
.transform(transforms.alias_in_join)
|
198
202
|
)
|
199
203
|
|
200
204
|
def _execute(
|
@@ -502,6 +506,7 @@ class FakeSnowflakeConnection:
|
|
502
506
|
create_database: bool = True,
|
503
507
|
create_schema: bool = True,
|
504
508
|
db_path: str | os.PathLike | None = None,
|
509
|
+
nop_regexes: list[str] | None = None,
|
505
510
|
*args: Any,
|
506
511
|
**kwargs: Any,
|
507
512
|
):
|
@@ -513,6 +518,7 @@ class FakeSnowflakeConnection:
|
|
513
518
|
self.database_set = False
|
514
519
|
self.schema_set = False
|
515
520
|
self.db_path = db_path
|
521
|
+
self.nop_regexes = nop_regexes
|
516
522
|
self._paramstyle = snowflake.connector.paramstyle
|
517
523
|
|
518
524
|
create_global_database(duck_conn)
|
fakesnow/transforms.py
CHANGED
@@ -13,6 +13,25 @@ MISSING_DATABASE = "missing_database"
|
|
13
13
|
SUCCESS_NOP = sqlglot.parse_one("SELECT 'Statement executed successfully.'")
|
14
14
|
|
15
15
|
|
16
|
+
def alias_in_join(expression: exp.Expression) -> exp.Expression:
|
17
|
+
if (
|
18
|
+
isinstance(expression, exp.Select)
|
19
|
+
and (aliases := {e.args.get("alias"): e for e in expression.expressions if isinstance(e, exp.Alias)})
|
20
|
+
and (joins := expression.args.get("joins"))
|
21
|
+
):
|
22
|
+
j: exp.Join
|
23
|
+
for j in joins:
|
24
|
+
if (
|
25
|
+
(on := j.args.get("on"))
|
26
|
+
and (col := on.this)
|
27
|
+
and (isinstance(col, exp.Column))
|
28
|
+
and (alias := aliases.get(col.this))
|
29
|
+
):
|
30
|
+
col.args["this"] = alias.this
|
31
|
+
|
32
|
+
return expression
|
33
|
+
|
34
|
+
|
16
35
|
def array_size(expression: exp.Expression) -> exp.Expression:
|
17
36
|
if isinstance(expression, exp.ArraySize):
|
18
37
|
# case is used to convert 0 to null, because null is returned by duckdb when no case matches
|
@@ -350,17 +369,13 @@ def extract_comment_on_table(expression: exp.Expression) -> exp.Expression:
|
|
350
369
|
return new
|
351
370
|
elif (
|
352
371
|
isinstance(expression, exp.AlterTable)
|
353
|
-
and (sexp := expression.find(exp.
|
354
|
-
and
|
355
|
-
and (
|
356
|
-
and (eid := eq.find(exp.Identifier))
|
357
|
-
and isinstance(eid.this, str)
|
358
|
-
and eid.this.upper() == "COMMENT"
|
359
|
-
and (lit := eq.find(exp.Literal))
|
372
|
+
and (sexp := expression.find(exp.AlterSet))
|
373
|
+
and (scp := sexp.find(exp.SchemaCommentProperty))
|
374
|
+
and isinstance(scp.this, exp.Literal)
|
360
375
|
and (table := expression.find(exp.Table))
|
361
376
|
):
|
362
377
|
new = SUCCESS_NOP.copy()
|
363
|
-
new.args["table_comment"] = (table,
|
378
|
+
new.args["table_comment"] = (table, scp.this.this)
|
364
379
|
return new
|
365
380
|
|
366
381
|
return expression
|
@@ -596,15 +611,12 @@ def json_extract_cast_as_varchar(expression: exp.Expression) -> exp.Expression:
|
|
596
611
|
"""
|
597
612
|
if (
|
598
613
|
isinstance(expression, exp.Cast)
|
599
|
-
and (to := expression.to)
|
600
|
-
and isinstance(to, exp.DataType)
|
601
|
-
and to.this in {exp.DataType.Type.VARCHAR, exp.DataType.Type.TEXT}
|
602
614
|
and (je := expression.this)
|
603
615
|
and isinstance(je, exp.JSONExtract)
|
604
616
|
and (path := je.expression)
|
605
617
|
and isinstance(path, exp.JSONPath)
|
606
618
|
):
|
607
|
-
|
619
|
+
je.replace(exp.JSONExtractScalar(this=je.this, expression=path))
|
608
620
|
return expression
|
609
621
|
|
610
622
|
|
@@ -937,7 +949,7 @@ def tag(expression: exp.Expression) -> exp.Expression:
|
|
937
949
|
|
938
950
|
if isinstance(expression, exp.AlterTable) and (actions := expression.args.get("actions")):
|
939
951
|
for a in actions:
|
940
|
-
if isinstance(a, exp.
|
952
|
+
if isinstance(a, exp.AlterSet) and a.args.get("tag"):
|
941
953
|
return SUCCESS_NOP
|
942
954
|
elif (
|
943
955
|
isinstance(expression, exp.Command)
|
@@ -947,6 +959,13 @@ def tag(expression: exp.Expression) -> exp.Expression:
|
|
947
959
|
):
|
948
960
|
# alter table modify column set tag
|
949
961
|
return SUCCESS_NOP
|
962
|
+
elif (
|
963
|
+
isinstance(expression, exp.Create)
|
964
|
+
and (kind := expression.args.get("kind"))
|
965
|
+
and isinstance(kind, str)
|
966
|
+
and kind.upper() == "TAG"
|
967
|
+
):
|
968
|
+
return SUCCESS_NOP
|
950
969
|
|
951
970
|
return expression
|
952
971
|
|
@@ -1114,20 +1133,16 @@ def to_timestamp_ntz(expression: exp.Expression) -> exp.Expression:
|
|
1114
1133
|
return expression
|
1115
1134
|
|
1116
1135
|
|
1117
|
-
def
|
1118
|
-
"""Convert timestamp_ntz(
|
1136
|
+
def timestamp_ntz(expression: exp.Expression) -> exp.Expression:
|
1137
|
+
"""Convert timestamp_ntz (snowflake) to timestamp (duckdb).
|
1119
1138
|
|
1120
|
-
|
1139
|
+
NB: timestamp_ntz defaults to nanosecond precision (ie: NTZ(9)). The duckdb equivalent is TIMESTAMP_NS.
|
1140
|
+
However we use TIMESTAMP (ie: microsecond precision) here rather than TIMESTAMP_NS to avoid
|
1141
|
+
https://github.com/duckdb/duckdb/issues/7980 in test_write_pandas_timestamp_ntz.
|
1121
1142
|
"""
|
1122
1143
|
|
1123
|
-
if (
|
1124
|
-
|
1125
|
-
and expression.this == exp.DataType.Type.TIMESTAMP
|
1126
|
-
and exp.DataTypeParam(this=exp.Literal(this="9", is_string=False)) in expression.expressions
|
1127
|
-
):
|
1128
|
-
new = expression.copy()
|
1129
|
-
del new.args["expressions"]
|
1130
|
-
return new
|
1144
|
+
if isinstance(expression, exp.DataType) and expression.this == exp.DataType.Type.TIMESTAMPNTZ:
|
1145
|
+
return exp.DataType(this=exp.DataType.Type.TIMESTAMP)
|
1131
1146
|
|
1132
1147
|
return expression
|
1133
1148
|
|
@@ -1175,7 +1190,6 @@ def try_parse_json(expression: exp.Expression) -> exp.Expression:
|
|
1175
1190
|
return expression
|
1176
1191
|
|
1177
1192
|
|
1178
|
-
# sqlglot.parse_one("create table example(date TIMESTAMP_NTZ(9));", read="snowflake")
|
1179
1193
|
def semi_structured_types(expression: exp.Expression) -> exp.Expression:
|
1180
1194
|
"""Convert OBJECT, ARRAY, and VARIANT types to duckdb compatible types.
|
1181
1195
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fakesnow
|
3
|
-
Version: 0.9.
|
3
|
+
Version: 0.9.14
|
4
4
|
Summary: Fake Snowflake Connector for Python. Run, mock and test Snowflake DB locally.
|
5
5
|
License: Apache License
|
6
6
|
Version 2.0, January 2004
|
@@ -210,10 +210,10 @@ Classifier: License :: OSI Approved :: MIT License
|
|
210
210
|
Requires-Python: >=3.9
|
211
211
|
Description-Content-Type: text/markdown
|
212
212
|
License-File: LICENSE
|
213
|
-
Requires-Dist: duckdb ~=0.10.
|
213
|
+
Requires-Dist: duckdb ~=0.10.3
|
214
214
|
Requires-Dist: pyarrow
|
215
215
|
Requires-Dist: snowflake-connector-python
|
216
|
-
Requires-Dist: sqlglot ~=
|
216
|
+
Requires-Dist: sqlglot ~=24.1.0
|
217
217
|
Provides-Extra: dev
|
218
218
|
Requires-Dist: build ~=1.0 ; extra == 'dev'
|
219
219
|
Requires-Dist: pandas-stubs ; extra == 'dev'
|
@@ -1,18 +1,18 @@
|
|
1
|
-
fakesnow/__init__.py,sha256=
|
1
|
+
fakesnow/__init__.py,sha256=7040jPjD-bNJDU4Lel42CD_DU47oeu1IsY9Fx_DlslQ,3799
|
2
2
|
fakesnow/__main__.py,sha256=GDrGyNTvBFuqn_UfDjKs7b3LPtU6gDv1KwosVDrukIM,76
|
3
3
|
fakesnow/checks.py,sha256=-QMvdcrRbhN60rnzxLBJ0IkUBWyLR8gGGKKmCS0w9mA,2383
|
4
4
|
fakesnow/cli.py,sha256=9qfI-Ssr6mo8UmIlXkUAOz2z2YPBgDsrEVaZv9FjGFs,2201
|
5
5
|
fakesnow/expr.py,sha256=CAxuYIUkwI339DQIBzvFF0F-m1tcVGKEPA5rDTzmH9A,892
|
6
|
-
fakesnow/fakes.py,sha256=
|
6
|
+
fakesnow/fakes.py,sha256=JM3Tt4ivDC6ddrVioT66yK2irL-wPDDFIEjUBxTq6G8,30277
|
7
7
|
fakesnow/fixtures.py,sha256=G-NkVeruSQAJ7fvSS2fR2oysUn0Yra1pohHlOvacKEk,455
|
8
8
|
fakesnow/global_database.py,sha256=WTVIP1VhNvdCeX7TQncX1TRpGQU5rBf5Pbxim40zeSU,1399
|
9
9
|
fakesnow/info_schema.py,sha256=CdIcGXHEQ_kmEAzdQKvA-PX41LA6wlK-4p1J45qgKYA,6266
|
10
10
|
fakesnow/macros.py,sha256=pX1YJDnQOkFJSHYUjQ6ErEkYIKvFI6Ncz_au0vv1csA,265
|
11
11
|
fakesnow/py.typed,sha256=B-DLSjYBi7pkKjwxCSdpVj2J02wgfJr-E7B1wOUyxYU,80
|
12
|
-
fakesnow/transforms.py,sha256=
|
13
|
-
fakesnow-0.9.
|
14
|
-
fakesnow-0.9.
|
15
|
-
fakesnow-0.9.
|
16
|
-
fakesnow-0.9.
|
17
|
-
fakesnow-0.9.
|
18
|
-
fakesnow-0.9.
|
12
|
+
fakesnow/transforms.py,sha256=8DwHoJ09U2915DovAH_ul0_5WzlUkHYLLK1njyhER4s,51230
|
13
|
+
fakesnow-0.9.14.dist-info/LICENSE,sha256=kW-7NWIyaRMQiDpryfSmF2DObDZHGR1cJZ39s6B1Svg,11344
|
14
|
+
fakesnow-0.9.14.dist-info/METADATA,sha256=qbt6HAOnnIuoB3dZ-JayLpH-pHqVsCQ1l-Qu8lsxSP0,17840
|
15
|
+
fakesnow-0.9.14.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
16
|
+
fakesnow-0.9.14.dist-info/entry_points.txt,sha256=2riAUgu928ZIHawtO8EsfrMEJhi-EH-z_Vq7Q44xKPM,47
|
17
|
+
fakesnow-0.9.14.dist-info/top_level.txt,sha256=500evXI1IFX9so82cizGIEMHAb_dJNPaZvd2H9dcKTA,24
|
18
|
+
fakesnow-0.9.14.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|