fakesnow 0.9.6__py3-none-any.whl → 0.9.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.
- fakesnow/fakes.py +11 -2
- fakesnow/transforms.py +302 -39
- {fakesnow-0.9.6.dist-info → fakesnow-0.9.7.dist-info}/METADATA +3 -4
- {fakesnow-0.9.6.dist-info → fakesnow-0.9.7.dist-info}/RECORD +8 -8
- {fakesnow-0.9.6.dist-info → fakesnow-0.9.7.dist-info}/LICENSE +0 -0
- {fakesnow-0.9.6.dist-info → fakesnow-0.9.7.dist-info}/WHEEL +0 -0
- {fakesnow-0.9.6.dist-info → fakesnow-0.9.7.dist-info}/entry_points.txt +0 -0
- {fakesnow-0.9.6.dist-info → fakesnow-0.9.7.dist-info}/top_level.txt +0 -0
fakesnow/fakes.py
CHANGED
@@ -173,7 +173,7 @@ class FakeSnowflakeCursor:
|
|
173
173
|
.transform(transforms.drop_schema_cascade)
|
174
174
|
.transform(transforms.tag)
|
175
175
|
.transform(transforms.semi_structured_types)
|
176
|
-
.transform(transforms.
|
176
|
+
.transform(transforms.try_parse_json)
|
177
177
|
# indices_to_json_extract must be before regex_substr
|
178
178
|
.transform(transforms.indices_to_json_extract)
|
179
179
|
.transform(transforms.json_extract_cast_as_varchar)
|
@@ -185,6 +185,7 @@ class FakeSnowflakeCursor:
|
|
185
185
|
.transform(transforms.values_columns)
|
186
186
|
.transform(transforms.to_date)
|
187
187
|
.transform(transforms.to_decimal)
|
188
|
+
.transform(transforms.try_to_decimal)
|
188
189
|
.transform(transforms.to_timestamp_ntz)
|
189
190
|
.transform(transforms.to_timestamp)
|
190
191
|
.transform(transforms.object_construct)
|
@@ -196,6 +197,11 @@ class FakeSnowflakeCursor:
|
|
196
197
|
.transform(transforms.array_size)
|
197
198
|
.transform(transforms.random)
|
198
199
|
.transform(transforms.identifier)
|
200
|
+
.transform(transforms.array_agg_within_group)
|
201
|
+
.transform(transforms.array_agg_to_json)
|
202
|
+
.transform(transforms.dateadd_date_cast)
|
203
|
+
.transform(transforms.dateadd_string_literal_timestamp_cast)
|
204
|
+
.transform(transforms.datediff_string_literal_timestamp_cast)
|
199
205
|
.transform(lambda e: transforms.show_schemas(e, self._conn.database))
|
200
206
|
.transform(lambda e: transforms.show_objects_tables(e, self._conn.database))
|
201
207
|
# TODO collapse into a single show_keys function
|
@@ -204,6 +210,7 @@ class FakeSnowflakeCursor:
|
|
204
210
|
.transform(lambda e: transforms.show_keys(e, self._conn.database, kind="FOREIGN"))
|
205
211
|
.transform(transforms.show_users)
|
206
212
|
.transform(transforms.create_user)
|
213
|
+
.transform(transforms.sha256)
|
207
214
|
)
|
208
215
|
sql = transformed.sql(dialect="duckdb")
|
209
216
|
result_sql = None
|
@@ -617,7 +624,9 @@ class FakeSnowflakeConnection:
|
|
617
624
|
# don't jsonify string
|
618
625
|
df[col] = df[col].apply(lambda x: json.dumps(x) if isinstance(x, (dict, list)) else x)
|
619
626
|
|
620
|
-
|
627
|
+
escaped_cols = ",".join(f'"{col}"' for col in df.columns.to_list())
|
628
|
+
self._duck_conn.execute(f"INSERT INTO {table_name}({escaped_cols}) SELECT * FROM df")
|
629
|
+
|
621
630
|
return self._duck_conn.fetchall()[0][0]
|
622
631
|
|
623
632
|
|
fakesnow/transforms.py
CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
from pathlib import Path
|
4
4
|
from string import Template
|
5
|
-
from typing import Literal, cast
|
5
|
+
from typing import ClassVar, Literal, cast
|
6
6
|
|
7
7
|
import sqlglot
|
8
8
|
from sqlglot import exp
|
@@ -22,6 +22,39 @@ def array_size(expression: exp.Expression) -> exp.Expression:
|
|
22
22
|
return expression
|
23
23
|
|
24
24
|
|
25
|
+
def array_agg_to_json(expression: exp.Expression) -> exp.Expression:
|
26
|
+
if isinstance(expression, exp.ArrayAgg):
|
27
|
+
return exp.Anonymous(this="TO_JSON", expressions=[expression])
|
28
|
+
|
29
|
+
return expression
|
30
|
+
|
31
|
+
|
32
|
+
def array_agg_within_group(expression: exp.Expression) -> exp.Expression:
|
33
|
+
"""Convert ARRAY_AGG(<expr>) WITHIN GROUP (<order-by-clause>) to ARRAY_AGG( <expr> <order-by-clause> )
|
34
|
+
Snowflake uses ARRAY_AGG(<expr>) WITHIN GROUP (ORDER BY <order-by-clause>)
|
35
|
+
to order the array, but DuckDB uses ARRAY_AGG( <expr> <order-by-clause> ).
|
36
|
+
See;
|
37
|
+
- https://docs.snowflake.com/en/sql-reference/functions/array_agg
|
38
|
+
- https://duckdb.org/docs/sql/aggregates.html#order-by-clause-in-aggregate-functions
|
39
|
+
Note; Snowflake has following restriction;
|
40
|
+
If you specify DISTINCT and WITHIN GROUP, both must refer to the same column.
|
41
|
+
Transformation does not handle this restriction.
|
42
|
+
"""
|
43
|
+
if (
|
44
|
+
isinstance(expression, exp.WithinGroup)
|
45
|
+
and (agg := expression.find(exp.ArrayAgg))
|
46
|
+
and (order := expression.expression)
|
47
|
+
):
|
48
|
+
return exp.ArrayAgg(
|
49
|
+
this=exp.Order(
|
50
|
+
this=agg.this,
|
51
|
+
expressions=order.expressions,
|
52
|
+
)
|
53
|
+
)
|
54
|
+
|
55
|
+
return expression
|
56
|
+
|
57
|
+
|
25
58
|
# TODO: move this into a Dialect as a transpilation
|
26
59
|
def create_database(expression: exp.Expression, db_path: Path | None = None) -> exp.Expression:
|
27
60
|
"""Transform create database to attach database.
|
@@ -136,6 +169,98 @@ def drop_schema_cascade(expression: exp.Expression) -> exp.Expression:
|
|
136
169
|
return new
|
137
170
|
|
138
171
|
|
172
|
+
def dateadd_date_cast(expression: exp.Expression) -> exp.Expression:
|
173
|
+
"""Cast result of DATEADD to DATE if the given expression is a cast to DATE
|
174
|
+
and unit is either DAY, WEEK, MONTH or YEAR to mimic Snowflake's DATEADD
|
175
|
+
behaviour.
|
176
|
+
|
177
|
+
Snowflake;
|
178
|
+
SELECT DATEADD(DAY, 3, '2023-03-03'::DATE) as D;
|
179
|
+
D: 2023-03-06 (DATE)
|
180
|
+
DuckDB;
|
181
|
+
SELECT CAST('2023-03-03' AS DATE) + INTERVAL 3 DAY AS D
|
182
|
+
D: 2023-03-06 00:00:00 (TIMESTAMP)
|
183
|
+
"""
|
184
|
+
|
185
|
+
if not isinstance(expression, exp.DateAdd):
|
186
|
+
return expression
|
187
|
+
|
188
|
+
if expression.unit is None:
|
189
|
+
return expression
|
190
|
+
|
191
|
+
if not isinstance(expression.unit.this, str):
|
192
|
+
return expression
|
193
|
+
|
194
|
+
if (unit := expression.unit.this.upper()) and unit.upper() not in {"DAY", "WEEK", "MONTH", "YEAR"}:
|
195
|
+
return expression
|
196
|
+
|
197
|
+
if not isinstance(expression.this, exp.Cast):
|
198
|
+
return expression
|
199
|
+
|
200
|
+
if expression.this.to.this != exp.DataType.Type.DATE:
|
201
|
+
return expression
|
202
|
+
|
203
|
+
return exp.Cast(
|
204
|
+
this=expression,
|
205
|
+
to=exp.DataType(this=exp.DataType.Type.DATE, nested=False, prefix=False),
|
206
|
+
)
|
207
|
+
|
208
|
+
|
209
|
+
def dateadd_string_literal_timestamp_cast(expression: exp.Expression) -> exp.Expression:
|
210
|
+
"""Snowflake's DATEADD function implicitly casts string literals to
|
211
|
+
timestamps regardless of unit.
|
212
|
+
"""
|
213
|
+
if not isinstance(expression, exp.DateAdd):
|
214
|
+
return expression
|
215
|
+
|
216
|
+
if not isinstance(expression.this, exp.Literal) or not expression.this.is_string:
|
217
|
+
return expression
|
218
|
+
|
219
|
+
new_dateadd = expression.copy()
|
220
|
+
new_dateadd.set(
|
221
|
+
"this",
|
222
|
+
exp.Cast(
|
223
|
+
this=expression.this,
|
224
|
+
# TODO: support TIMESTAMP_TYPE_MAPPING of TIMESTAMP_LTZ/TZ
|
225
|
+
to=exp.DataType(this=exp.DataType.Type.TIMESTAMP, nested=False, prefix=False),
|
226
|
+
),
|
227
|
+
)
|
228
|
+
|
229
|
+
return new_dateadd
|
230
|
+
|
231
|
+
|
232
|
+
def datediff_string_literal_timestamp_cast(expression: exp.Expression) -> exp.Expression:
|
233
|
+
"""Snowflake's DATEDIFF function implicitly casts string literals to
|
234
|
+
timestamps regardless of unit.
|
235
|
+
"""
|
236
|
+
|
237
|
+
if not isinstance(expression, exp.DateDiff):
|
238
|
+
return expression
|
239
|
+
|
240
|
+
op1 = expression.this.copy()
|
241
|
+
op2 = expression.expression.copy()
|
242
|
+
|
243
|
+
if isinstance(op1, exp.Literal) and op1.is_string:
|
244
|
+
op1 = exp.Cast(
|
245
|
+
this=op1,
|
246
|
+
# TODO: support TIMESTAMP_TYPE_MAPPING of TIMESTAMP_LTZ/TZ
|
247
|
+
to=exp.DataType(this=exp.DataType.Type.TIMESTAMP, nested=False, prefix=False),
|
248
|
+
)
|
249
|
+
|
250
|
+
if isinstance(op2, exp.Literal) and op2.is_string:
|
251
|
+
op2 = exp.Cast(
|
252
|
+
this=op2,
|
253
|
+
# TODO: support TIMESTAMP_TYPE_MAPPING of TIMESTAMP_LTZ/TZ
|
254
|
+
to=exp.DataType(this=exp.DataType.Type.TIMESTAMP, nested=False, prefix=False),
|
255
|
+
)
|
256
|
+
|
257
|
+
new_datediff = expression.copy()
|
258
|
+
new_datediff.set("this", op1)
|
259
|
+
new_datediff.set("expression", op2)
|
260
|
+
|
261
|
+
return new_datediff
|
262
|
+
|
263
|
+
|
139
264
|
def extract_comment_on_columns(expression: exp.Expression) -> exp.Expression:
|
140
265
|
"""Extract column comments, removing it from the Expression.
|
141
266
|
|
@@ -508,38 +633,26 @@ def object_construct(expression: exp.Expression) -> exp.Expression:
|
|
508
633
|
"""
|
509
634
|
|
510
635
|
if isinstance(expression, exp.Struct):
|
511
|
-
|
512
|
-
for
|
513
|
-
if
|
514
|
-
|
515
|
-
|
516
|
-
return exp.Anonymous(this="TO_JSON", expressions=[expression])
|
636
|
+
non_null_expressions = []
|
637
|
+
for e in expression.expressions:
|
638
|
+
if not (isinstance(e, exp.PropertyEQ)):
|
639
|
+
non_null_expressions.append(e)
|
640
|
+
continue
|
517
641
|
|
518
|
-
|
642
|
+
left = e.left
|
643
|
+
right = e.right
|
519
644
|
|
645
|
+
left_is_null = isinstance(left, exp.Null)
|
646
|
+
right_is_null = isinstance(right, exp.Null)
|
520
647
|
|
521
|
-
|
522
|
-
|
648
|
+
if left_is_null or right_is_null:
|
649
|
+
continue
|
523
650
|
|
524
|
-
|
525
|
-
>>> import sqlglot
|
526
|
-
>>> sqlglot.parse_one("insert into table1 (name) select parse_json('{}')").transform(parse_json).sql()
|
527
|
-
"CREATE TABLE table1 (name JSON)"
|
528
|
-
Args:
|
529
|
-
expression (exp.Expression): the expression that will be transformed.
|
651
|
+
non_null_expressions.append(e)
|
530
652
|
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
if (
|
536
|
-
isinstance(expression, exp.Anonymous)
|
537
|
-
and isinstance(expression.this, str)
|
538
|
-
and expression.this.upper() == "PARSE_JSON"
|
539
|
-
):
|
540
|
-
new = expression.copy()
|
541
|
-
new.args["this"] = "JSON"
|
542
|
-
return new
|
653
|
+
new_struct = expression.copy()
|
654
|
+
new_struct.set("expressions", non_null_expressions)
|
655
|
+
return exp.Anonymous(this="TO_JSON", expressions=[new_struct])
|
543
656
|
|
544
657
|
return expression
|
545
658
|
|
@@ -829,30 +942,107 @@ def to_date(expression: exp.Expression) -> exp.Expression:
|
|
829
942
|
return expression
|
830
943
|
|
831
944
|
|
945
|
+
def _get_to_number_args(e: exp.ToNumber) -> tuple[exp.Expression | None, exp.Expression | None, exp.Expression | None]:
|
946
|
+
arg_format = e.args.get("format")
|
947
|
+
arg_precision = e.args.get("precision")
|
948
|
+
arg_scale = e.args.get("scale")
|
949
|
+
|
950
|
+
_format = None
|
951
|
+
_precision = None
|
952
|
+
_scale = None
|
953
|
+
|
954
|
+
# to_number(value, <format>, <precision>, <scale>)
|
955
|
+
if arg_format:
|
956
|
+
if arg_format.is_string:
|
957
|
+
# to_number('100', 'TM9' ...)
|
958
|
+
_format = arg_format
|
959
|
+
|
960
|
+
# to_number('100', 'TM9', 10 ...)
|
961
|
+
if arg_precision:
|
962
|
+
_precision = arg_precision
|
963
|
+
|
964
|
+
# to_number('100', 'TM9', 10, 2)
|
965
|
+
if arg_scale:
|
966
|
+
_scale = arg_scale
|
967
|
+
else:
|
968
|
+
pass
|
969
|
+
else:
|
970
|
+
# to_number('100', 10, ...)
|
971
|
+
# arg_format is not a string, so it must be precision.
|
972
|
+
_precision = arg_format
|
973
|
+
|
974
|
+
# to_number('100', 10, 2)
|
975
|
+
# And arg_precision must be scale
|
976
|
+
if arg_precision:
|
977
|
+
_scale = arg_precision
|
978
|
+
else:
|
979
|
+
# If format is not provided, just check for precision and scale directly
|
980
|
+
if arg_precision:
|
981
|
+
_precision = arg_precision
|
982
|
+
if arg_scale:
|
983
|
+
_scale = arg_scale
|
984
|
+
|
985
|
+
return _format, _precision, _scale
|
986
|
+
|
987
|
+
|
988
|
+
def _to_decimal(expression: exp.Expression, cast_node: type[exp.Cast]) -> exp.Expression:
|
989
|
+
expressions: list[exp.Expression] = expression.expressions
|
990
|
+
|
991
|
+
if len(expressions) > 1 and expressions[1].is_string:
|
992
|
+
# see https://docs.snowflake.com/en/sql-reference/functions/to_decimal#arguments
|
993
|
+
raise NotImplementedError(f"{expression.this} with format argument")
|
994
|
+
|
995
|
+
precision = expressions[1] if len(expressions) > 1 else exp.Literal(this="38", is_string=False)
|
996
|
+
scale = expressions[2] if len(expressions) > 2 else exp.Literal(this="0", is_string=False)
|
997
|
+
|
998
|
+
return cast_node(
|
999
|
+
this=expressions[0],
|
1000
|
+
to=exp.DataType(this=exp.DataType.Type.DECIMAL, expressions=[precision, scale], nested=False, prefix=False),
|
1001
|
+
)
|
1002
|
+
|
1003
|
+
|
832
1004
|
def to_decimal(expression: exp.Expression) -> exp.Expression:
|
833
1005
|
"""Transform to_decimal, to_number, to_numeric expressions from snowflake to duckdb.
|
834
1006
|
|
835
1007
|
See https://docs.snowflake.com/en/sql-reference/functions/to_decimal
|
836
1008
|
"""
|
837
1009
|
|
1010
|
+
if isinstance(expression, exp.ToNumber):
|
1011
|
+
format_, precision, scale = _get_to_number_args(expression)
|
1012
|
+
if format_:
|
1013
|
+
raise NotImplementedError(f"{expression.this} with format argument")
|
1014
|
+
|
1015
|
+
if not precision:
|
1016
|
+
precision = exp.Literal(this="38", is_string=False)
|
1017
|
+
if not scale:
|
1018
|
+
scale = exp.Literal(this="0", is_string=False)
|
1019
|
+
|
1020
|
+
return exp.Cast(
|
1021
|
+
this=expression.this,
|
1022
|
+
to=exp.DataType(this=exp.DataType.Type.DECIMAL, expressions=[precision, scale], nested=False, prefix=False),
|
1023
|
+
)
|
1024
|
+
|
838
1025
|
if (
|
839
1026
|
isinstance(expression, exp.Anonymous)
|
840
1027
|
and isinstance(expression.this, str)
|
841
|
-
and expression.this.upper() in ["TO_DECIMAL", "
|
1028
|
+
and expression.this.upper() in ["TO_DECIMAL", "TO_NUMERIC"]
|
842
1029
|
):
|
843
|
-
|
1030
|
+
return _to_decimal(expression, exp.Cast)
|
844
1031
|
|
845
|
-
|
846
|
-
# see https://docs.snowflake.com/en/sql-reference/functions/to_decimal#arguments
|
847
|
-
raise NotImplementedError(f"{expression.this} with format argument")
|
1032
|
+
return expression
|
848
1033
|
|
849
|
-
precision = expressions[1] if len(expressions) > 1 else exp.Literal(this="38", is_string=False)
|
850
|
-
scale = expressions[2] if len(expressions) > 2 else exp.Literal(this="0", is_string=False)
|
851
1034
|
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
1035
|
+
def try_to_decimal(expression: exp.Expression) -> exp.Expression:
|
1036
|
+
"""Transform try_to_decimal, try_to_number, try_to_numeric expressions from snowflake to duckdb.
|
1037
|
+
See https://docs.snowflake.com/en/sql-reference/functions/try_to_decimal
|
1038
|
+
"""
|
1039
|
+
|
1040
|
+
if (
|
1041
|
+
isinstance(expression, exp.Anonymous)
|
1042
|
+
and isinstance(expression.this, str)
|
1043
|
+
and expression.this.upper() in ["TRY_TO_DECIMAL", "TRY_TO_NUMBER", "TRY_TO_NUMERIC"]
|
1044
|
+
):
|
1045
|
+
return _to_decimal(expression, exp.TryCast)
|
856
1046
|
|
857
1047
|
return expression
|
858
1048
|
|
@@ -905,6 +1095,34 @@ def timestamp_ntz_ns(expression: exp.Expression) -> exp.Expression:
|
|
905
1095
|
return expression
|
906
1096
|
|
907
1097
|
|
1098
|
+
def try_parse_json(expression: exp.Expression) -> exp.Expression:
|
1099
|
+
"""Convert TRY_PARSE_JSON() to TRY_CAST(... as JSON).
|
1100
|
+
|
1101
|
+
Example:
|
1102
|
+
>>> import sqlglot
|
1103
|
+
>>> sqlglot.parse_one("select try_parse_json('{}')").transform(parse_json).sql()
|
1104
|
+
"SELECT TRY_CAST('{}' AS JSON)"
|
1105
|
+
Args:
|
1106
|
+
expression (exp.Expression): the expression that will be transformed.
|
1107
|
+
|
1108
|
+
Returns:
|
1109
|
+
exp.Expression: The transformed expression.
|
1110
|
+
"""
|
1111
|
+
|
1112
|
+
if (
|
1113
|
+
isinstance(expression, exp.Anonymous)
|
1114
|
+
and isinstance(expression.this, str)
|
1115
|
+
and expression.this.upper() == "TRY_PARSE_JSON"
|
1116
|
+
):
|
1117
|
+
expressions = expression.expressions
|
1118
|
+
return exp.TryCast(
|
1119
|
+
this=expressions[0],
|
1120
|
+
to=exp.DataType(this=exp.DataType.Type.JSON, nested=False),
|
1121
|
+
)
|
1122
|
+
|
1123
|
+
return expression
|
1124
|
+
|
1125
|
+
|
908
1126
|
# sqlglot.parse_one("create table example(date TIMESTAMP_NTZ(9));", read="snowflake")
|
909
1127
|
def semi_structured_types(expression: exp.Expression) -> exp.Expression:
|
910
1128
|
"""Convert OBJECT, ARRAY, and VARIANT types to duckdb compatible types.
|
@@ -1087,3 +1305,48 @@ def show_keys(
|
|
1087
1305
|
raise NotImplementedError(f"SHOW PRIMARY KEYS with {scope_kind} not yet supported")
|
1088
1306
|
return sqlglot.parse_one(statement)
|
1089
1307
|
return expression
|
1308
|
+
|
1309
|
+
|
1310
|
+
class SHA256(exp.Func):
|
1311
|
+
_sql_names: ClassVar = ["SHA256"]
|
1312
|
+
arg_types: ClassVar = {"this": True}
|
1313
|
+
|
1314
|
+
|
1315
|
+
def sha256(expression: exp.Expression) -> exp.Expression:
|
1316
|
+
"""Convert sha2() or sha2_hex() to sha256().
|
1317
|
+
|
1318
|
+
Convert sha2_binary() to unhex(sha256()).
|
1319
|
+
|
1320
|
+
Example:
|
1321
|
+
>>> import sqlglot
|
1322
|
+
>>> sqlglot.parse_one("insert into table1 (name) select sha2('foo')").transform(sha256).sql()
|
1323
|
+
"INSERT INTO table1 (name) SELECT SHA256('foo')"
|
1324
|
+
Args:
|
1325
|
+
expression (exp.Expression): the expression that will be transformed.
|
1326
|
+
|
1327
|
+
Returns:
|
1328
|
+
exp.Expression: The transformed expression.
|
1329
|
+
"""
|
1330
|
+
|
1331
|
+
if isinstance(expression, exp.SHA2) and expression.args.get("length", exp.Literal.number(256)).this == "256":
|
1332
|
+
return SHA256(this=expression.this)
|
1333
|
+
elif (
|
1334
|
+
isinstance(expression, exp.Anonymous)
|
1335
|
+
and expression.this.upper() == "SHA2_HEX"
|
1336
|
+
and (
|
1337
|
+
len(expression.expressions) == 1
|
1338
|
+
or (len(expression.expressions) == 2 and expression.expressions[1].this == "256")
|
1339
|
+
)
|
1340
|
+
):
|
1341
|
+
return SHA256(this=expression.expressions[0])
|
1342
|
+
elif (
|
1343
|
+
isinstance(expression, exp.Anonymous)
|
1344
|
+
and expression.this.upper() == "SHA2_BINARY"
|
1345
|
+
and (
|
1346
|
+
len(expression.expressions) == 1
|
1347
|
+
or (len(expression.expressions) == 2 and expression.expressions[1].this == "256")
|
1348
|
+
)
|
1349
|
+
):
|
1350
|
+
return exp.Unhex(this=SHA256(this=expression.expressions[0]))
|
1351
|
+
|
1352
|
+
return expression
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fakesnow
|
3
|
-
Version: 0.9.
|
3
|
+
Version: 0.9.7
|
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
|
@@ -213,7 +213,7 @@ License-File: LICENSE
|
|
213
213
|
Requires-Dist: duckdb ~=0.10.0
|
214
214
|
Requires-Dist: pyarrow
|
215
215
|
Requires-Dist: snowflake-connector-python
|
216
|
-
Requires-Dist: sqlglot ~=
|
216
|
+
Requires-Dist: sqlglot ~=23.3.0
|
217
217
|
Provides-Extra: dev
|
218
218
|
Requires-Dist: build ~=1.0 ; extra == 'dev'
|
219
219
|
Requires-Dist: pandas-stubs ; extra == 'dev'
|
@@ -233,8 +233,7 @@ Requires-Dist: jupysql ; extra == 'notebook'
|
|
233
233
|
[](https://github.com/tekumara/fakesnow/actions/workflows/ci.yml)
|
234
234
|
[](https://github.com/tekumara/fakesnow/actions/workflows/release.yml)
|
235
235
|
[](https://pypi.org/project/fakesnow/)
|
236
|
-
|
237
|
-
[](../../actions/workflows/ci.yml)
|
236
|
+
[](https://pypi.org/project/fakesnow/)
|
238
237
|
|
239
238
|
Fake [Snowflake Connector for Python](https://docs.snowflake.com/en/user-guide/python-connector). Run and mock Snowflake DB locally.
|
240
239
|
|
@@ -3,16 +3,16 @@ 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=3tTPaAC1vBaTLmSG92o51QA0AzIT9XDieYiZsMzvY9M,28929
|
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=77hqWLWsZNvi6fLrn-JhIIeDy8CgiJ-zlNIAm8rQLf0,48818
|
13
|
+
fakesnow-0.9.7.dist-info/LICENSE,sha256=kW-7NWIyaRMQiDpryfSmF2DObDZHGR1cJZ39s6B1Svg,11344
|
14
|
+
fakesnow-0.9.7.dist-info/METADATA,sha256=ISDnq1yQPohGORq0isidKp11g_vWYt37rdtWz2vaoKE,17831
|
15
|
+
fakesnow-0.9.7.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
16
|
+
fakesnow-0.9.7.dist-info/entry_points.txt,sha256=2riAUgu928ZIHawtO8EsfrMEJhi-EH-z_Vq7Q44xKPM,47
|
17
|
+
fakesnow-0.9.7.dist-info/top_level.txt,sha256=500evXI1IFX9so82cizGIEMHAb_dJNPaZvd2H9dcKTA,24
|
18
|
+
fakesnow-0.9.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|