fakesnow 0.9.25__py3-none-any.whl → 0.9.27__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/cursor.py CHANGED
@@ -162,6 +162,7 @@ class FakeSnowflakeCursor:
162
162
  .transform(transforms.extract_comment_on_columns)
163
163
  .transform(transforms.information_schema_fs_columns_snowflake)
164
164
  .transform(transforms.information_schema_fs_tables_ext)
165
+ .transform(transforms.information_schema_fs_views)
165
166
  .transform(transforms.drop_schema_cascade)
166
167
  .transform(transforms.tag)
167
168
  .transform(transforms.semi_structured_types)
@@ -294,6 +295,9 @@ class FakeSnowflakeCursor:
294
295
  (affected_count,) = self._duck_conn.fetchall()[0]
295
296
  result_sql = SQL_DELETED_ROWS.substitute(count=affected_count)
296
297
 
298
+ elif cmd == "TRUNCATETABLE":
299
+ result_sql = SQL_SUCCESS
300
+
297
301
  elif cmd in ("DESCRIBE TABLE", "DESCRIBE VIEW"):
298
302
  # DESCRIBE TABLE/VIEW has already been run above to detect and error if the table exists
299
303
  # We now rerun DESCRIBE TABLE/VIEW but transformed with columns to match Snowflake
fakesnow/info_schema.py CHANGED
@@ -102,7 +102,7 @@ where catalog_name not in ('memory', 'system', 'temp', '_fs_global')
102
102
  # replicates https://docs.snowflake.com/sql-reference/info-schema/views
103
103
  SQL_CREATE_INFORMATION_SCHEMA_VIEWS_VIEW = Template(
104
104
  """
105
- create view if not exists ${catalog}.information_schema.views AS
105
+ create view if not exists ${catalog}.information_schema._fs_views AS
106
106
  select
107
107
  database_name as table_catalog,
108
108
  schema_name as table_schema,
fakesnow/transforms.py CHANGED
@@ -28,6 +28,8 @@ def alias_in_join(expression: exp.Expression) -> exp.Expression:
28
28
  and (col := on.this)
29
29
  and (isinstance(col, exp.Column))
30
30
  and (alias := aliases.get(col.this))
31
+ # don't rewrite col with table identifier
32
+ and not col.table
31
33
  ):
32
34
  col.args["this"] = alias.this
33
35
 
@@ -37,7 +39,7 @@ def alias_in_join(expression: exp.Expression) -> exp.Expression:
37
39
  def alter_table_strip_cluster_by(expression: exp.Expression) -> exp.Expression:
38
40
  """Turn alter table cluster by into a no-op"""
39
41
  if (
40
- isinstance(expression, exp.AlterTable)
42
+ isinstance(expression, exp.Alter)
41
43
  and (actions := expression.args.get("actions"))
42
44
  and len(actions) == 1
43
45
  and (isinstance(actions[0], exp.Cluster))
@@ -356,7 +358,7 @@ def extract_comment_on_columns(expression: exp.Expression) -> exp.Expression:
356
358
  exp.Expression: The transformed expression, with any comment stored in the new 'table_comment' arg.
357
359
  """
358
360
 
359
- if isinstance(expression, exp.AlterTable) and (actions := expression.args.get("actions")):
361
+ if isinstance(expression, exp.Alter) and (actions := expression.args.get("actions")):
360
362
  new_actions: list[exp.Expression] = []
361
363
  col_comments: list[tuple[str, str]] = []
362
364
  for a in actions:
@@ -410,7 +412,7 @@ def extract_comment_on_table(expression: exp.Expression) -> exp.Expression:
410
412
  new.args["table_comment"] = (table, cexp.this)
411
413
  return new
412
414
  elif (
413
- isinstance(expression, exp.AlterTable)
415
+ isinstance(expression, exp.Alter)
414
416
  and (sexp := expression.find(exp.AlterSet))
415
417
  and (scp := sexp.find(exp.SchemaCommentProperty))
416
418
  and isinstance(scp.this, exp.Literal)
@@ -436,7 +438,7 @@ def extract_text_length(expression: exp.Expression) -> exp.Expression:
436
438
  exp.Expression: The original expression, with any text lengths stored in the new 'text_lengths' arg.
437
439
  """
438
440
 
439
- if isinstance(expression, (exp.Create, exp.AlterTable)):
441
+ if isinstance(expression, (exp.Create, exp.Alter)):
440
442
  text_lengths = []
441
443
 
442
444
  # exp.Select is for a ctas, exp.Schema is a plain definition
@@ -471,7 +473,6 @@ def flatten(expression: exp.Expression) -> exp.Expression:
471
473
 
472
474
  See https://docs.snowflake.com/en/sql-reference/functions/flatten
473
475
 
474
- TODO: return index.
475
476
  TODO: support objects.
476
477
  """
477
478
  if (
@@ -483,20 +484,34 @@ def flatten(expression: exp.Expression) -> exp.Expression:
483
484
  ):
484
485
  explode_expression = expression.this.this.expression
485
486
 
486
- return exp.Lateral(
487
- this=exp.Unnest(
487
+ value = exp.Cast(
488
+ this=explode_expression,
489
+ to=exp.DataType(
490
+ this=exp.DataType.Type.ARRAY,
491
+ expressions=[exp.DataType(this=exp.DataType.Type.JSON, nested=False, prefix=False)],
492
+ nested=True,
493
+ ),
494
+ )
495
+
496
+ return exp.Subquery(
497
+ this=exp.Select(
488
498
  expressions=[
489
- exp.Cast(
490
- this=explode_expression,
491
- to=exp.DataType(
492
- this=exp.DataType.Type.ARRAY,
493
- expressions=[exp.DataType(this=exp.DataType.Type.JSON, nested=False, prefix=False)],
494
- nested=True,
499
+ exp.Unnest(
500
+ expressions=[value],
501
+ alias=exp.Identifier(this="VALUE", quoted=False),
502
+ ),
503
+ exp.Alias(
504
+ this=exp.Sub(
505
+ this=exp.Anonymous(
506
+ this="generate_subscripts", expressions=[value, exp.Literal(this="1", is_string=False)]
507
+ ),
508
+ expression=exp.Literal(this="1", is_string=False),
495
509
  ),
496
- )
510
+ alias=exp.Identifier(this="INDEX", quoted=False),
511
+ ),
497
512
  ],
498
513
  ),
499
- alias=exp.TableAlias(this=alias.this, columns=[exp.Identifier(this="VALUE", quoted=False)]),
514
+ alias=exp.TableAlias(this=alias.this),
500
515
  )
501
516
 
502
517
  return expression
@@ -622,6 +637,20 @@ def information_schema_fs_tables_ext(expression: exp.Expression) -> exp.Expressi
622
637
  return expression
623
638
 
624
639
 
640
+ def information_schema_fs_views(expression: exp.Expression) -> exp.Expression:
641
+ """Use information_schema._fs_views to return Snowflake's version instead of duckdb's."""
642
+
643
+ if (
644
+ isinstance(expression, exp.Select)
645
+ and (tbl_exp := expression.find(exp.Table))
646
+ and tbl_exp.name.upper() == "VIEWS"
647
+ and tbl_exp.db.upper() == "INFORMATION_SCHEMA"
648
+ ):
649
+ tbl_exp.set("this", exp.Identifier(this="_FS_VIEWS", quoted=False))
650
+
651
+ return expression
652
+
653
+
625
654
  def integer_precision(expression: exp.Expression) -> exp.Expression:
626
655
  """Convert integers to bigint.
627
656
 
@@ -631,8 +660,7 @@ def integer_precision(expression: exp.Expression) -> exp.Expression:
631
660
  if (
632
661
  isinstance(expression, exp.DataType)
633
662
  and (expression.this == exp.DataType.Type.DECIMAL and not expression.expressions)
634
- or expression.this in (exp.DataType.Type.INT, exp.DataType.Type.SMALLINT, exp.DataType.Type.TINYINT)
635
- ):
663
+ ) or expression.this in (exp.DataType.Type.INT, exp.DataType.Type.SMALLINT, exp.DataType.Type.TINYINT):
636
664
  return exp.DataType(
637
665
  this=exp.DataType.Type.BIGINT,
638
666
  nested=False,
@@ -707,8 +735,8 @@ def random(expression: exp.Expression) -> exp.Expression:
707
735
  new_rand = exp.Cast(
708
736
  this=exp.Paren(
709
737
  this=exp.Mul(
710
- this=exp.Paren(this=exp.Sub(this=exp.Rand(), expression=exp.Literal(this=0.5, is_string=False))),
711
- expression=exp.Literal(this=9223372036854775807, is_string=False),
738
+ this=exp.Paren(this=exp.Sub(this=exp.Rand(), expression=exp.Literal(this="0.5", is_string=False))),
739
+ expression=exp.Literal(this="9223372036854775807", is_string=False),
712
740
  )
713
741
  ),
714
742
  to=exp.DataType(this=exp.DataType.Type.BIGINT, nested=False, prefix=False),
@@ -809,31 +837,24 @@ def regex_substr(expression: exp.Expression) -> exp.Expression:
809
837
  pattern.args["this"] = pattern.this.replace("\\\\", "\\")
810
838
 
811
839
  # number of characters from the beginning of the string where the function starts searching for matches
812
- try:
813
- position = expression.args["position"]
814
- except KeyError:
815
- position = exp.Literal(this="1", is_string=False)
840
+ position = expression.args["position"] or exp.Literal(this="1", is_string=False)
816
841
 
817
842
  # which occurrence of the pattern to match
818
- try:
819
- occurrence = int(expression.args["occurrence"].this)
820
- except KeyError:
821
- occurrence = 1
843
+ occurrence = expression.args["occurrence"]
844
+ occurrence = int(occurrence.this) if occurrence else 1
822
845
 
823
846
  # the duckdb dialect increments bracket (ie: index) expressions by 1 because duckdb is 1-indexed,
824
847
  # so we need to compensate by subtracting 1
825
848
  occurrence = exp.Literal(this=str(occurrence - 1), is_string=False)
826
849
 
827
- try:
828
- regex_parameters_value = str(expression.args["parameters"].this)
850
+ if parameters := expression.args["parameters"]:
829
851
  # 'e' parameter doesn't make sense for duckdb
830
- regex_parameters = exp.Literal(this=regex_parameters_value.replace("e", ""), is_string=True)
831
- except KeyError:
852
+ regex_parameters = exp.Literal(this=parameters.this.replace("e", ""), is_string=True)
853
+ else:
832
854
  regex_parameters = exp.Literal(is_string=True)
833
855
 
834
- try:
835
- group_num = expression.args["group"]
836
- except KeyError:
856
+ group_num = expression.args["group"]
857
+ if not group_num:
837
858
  if isinstance(regex_parameters.this, str) and "e" in regex_parameters.this:
838
859
  group_num = exp.Literal(this="1", is_string=False)
839
860
  else:
@@ -1023,7 +1044,7 @@ def tag(expression: exp.Expression) -> exp.Expression:
1023
1044
  exp.Expression: The transformed expression.
1024
1045
  """
1025
1046
 
1026
- if isinstance(expression, exp.AlterTable) and (actions := expression.args.get("actions")):
1047
+ if isinstance(expression, exp.Alter) and (actions := expression.args.get("actions")):
1027
1048
  for a in actions:
1028
1049
  if isinstance(a, exp.AlterSet) and a.args.get("tag"):
1029
1050
  return SUCCESS_NOP
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fakesnow
3
- Version: 0.9.25
3
+ Version: 0.9.27
4
4
  Summary: Fake Snowflake Connector for Python. Run, mock and test Snowflake DB locally.
5
- License: Apache License
5
+ License: Apache License
6
6
  Version 2.0, January 2004
7
7
  http://www.apache.org/licenses/
8
8
 
@@ -210,22 +210,22 @@ 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~=1.0.0
213
+ Requires-Dist: duckdb~=1.1.3
214
214
  Requires-Dist: pyarrow
215
215
  Requires-Dist: snowflake-connector-python
216
- Requires-Dist: sqlglot~=25.9.0
216
+ Requires-Dist: sqlglot~=25.34.0
217
217
  Provides-Extra: dev
218
218
  Requires-Dist: build~=1.0; extra == "dev"
219
219
  Requires-Dist: dirty-equals; extra == "dev"
220
220
  Requires-Dist: pandas-stubs; extra == "dev"
221
221
  Requires-Dist: snowflake-connector-python[pandas,secure-local-storage]; extra == "dev"
222
- Requires-Dist: pre-commit~=3.4; extra == "dev"
222
+ Requires-Dist: pre-commit~=4.0; extra == "dev"
223
223
  Requires-Dist: pyarrow-stubs==10.0.1.9; extra == "dev"
224
224
  Requires-Dist: pytest~=8.0; extra == "dev"
225
225
  Requires-Dist: pytest-asyncio; extra == "dev"
226
- Requires-Dist: ruff~=0.6.3; extra == "dev"
227
- Requires-Dist: twine~=5.0; extra == "dev"
228
- Requires-Dist: snowflake-sqlalchemy~=1.5.0; extra == "dev"
226
+ Requires-Dist: ruff~=0.8.1; extra == "dev"
227
+ Requires-Dist: twine~=6.0; extra == "dev"
228
+ Requires-Dist: snowflake-sqlalchemy~=1.7.0; extra == "dev"
229
229
  Provides-Extra: notebook
230
230
  Requires-Dist: duckdb-engine; extra == "notebook"
231
231
  Requires-Dist: ipykernel; extra == "notebook"
@@ -4,23 +4,23 @@ fakesnow/arrow.py,sha256=EGAYeuCnRuvmWBEGqw2YOcgQR4zcCsZBu85kSRl70dQ,4698
4
4
  fakesnow/checks.py,sha256=N8sXldhS3u1gG32qvZ4VFlsKgavRKrQrxLiQU8am1lw,2691
5
5
  fakesnow/cli.py,sha256=9qfI-Ssr6mo8UmIlXkUAOz2z2YPBgDsrEVaZv9FjGFs,2201
6
6
  fakesnow/conn.py,sha256=Gy_Z7BZRm5yMjV3x6hR4iegDQFdG9aJBjqWdc3iWYFU,5353
7
- fakesnow/cursor.py,sha256=JbLSTzIN5Hu6ECn1kQ8-hC8V-ENeEWTID4JxHbGpEIo,19948
7
+ fakesnow/cursor.py,sha256=8wWtRCxzrM1yiHmH2C-9CT0b98nTzr23ygeaEAkumRE,20086
8
8
  fakesnow/expr.py,sha256=CAxuYIUkwI339DQIBzvFF0F-m1tcVGKEPA5rDTzmH9A,892
9
9
  fakesnow/fakes.py,sha256=JQTiUkkwPeQrJ8FDWhPFPK6pGwd_aR2oiOrNzCWznlM,187
10
10
  fakesnow/fixtures.py,sha256=G-NkVeruSQAJ7fvSS2fR2oysUn0Yra1pohHlOvacKEk,455
11
- fakesnow/info_schema.py,sha256=DObVOrhzppAFHsdtj4YI9oRISn9SkJUG6ONjVleQQ_Y,6303
11
+ fakesnow/info_schema.py,sha256=nsDceFtjiSXrvkksKziVvqrefskaSyOmAspBwMAsaDg,6307
12
12
  fakesnow/instance.py,sha256=3cJvPRuFy19dMKXbtBLl6imzO48pEw8uTYhZyFDuwhk,3133
13
13
  fakesnow/macros.py,sha256=pX1YJDnQOkFJSHYUjQ6ErEkYIKvFI6Ncz_au0vv1csA,265
14
14
  fakesnow/pandas_tools.py,sha256=WjyjTV8QUCQQaCGboaEOvx2uo4BkknpWYjtLwkeCY6U,3468
15
15
  fakesnow/py.typed,sha256=B-DLSjYBi7pkKjwxCSdpVj2J02wgfJr-E7B1wOUyxYU,80
16
16
  fakesnow/server.py,sha256=SO5xKZ4rvySsuKDsoSPSCZcFuIX_K7d1XJYhRRJ-7Bk,4150
17
- fakesnow/transforms.py,sha256=hPQ9L1TvsDjFiGbs8mGUnIFyPUR7JxFU8FZRKFj5ZD0,54568
17
+ fakesnow/transforms.py,sha256=h35WHWxhlXpfe03f3FCF64QVmiCLMdDZF7oZmaAOS60,55451
18
18
  fakesnow/transforms_merge.py,sha256=7rq-UPjfFNRrFsqR8xx3otwP6-k4eslLVLhfuqSXq1A,8314
19
19
  fakesnow/types.py,sha256=9Tt83Z7ctc9_v6SYyayXYz4MEI4RZo4zq_uqdj4g3Dk,2681
20
20
  fakesnow/variables.py,sha256=WXyPnkeNwD08gy52yF66CVe2twiYC50tztNfgXV4q1k,3032
21
- fakesnow-0.9.25.dist-info/LICENSE,sha256=kW-7NWIyaRMQiDpryfSmF2DObDZHGR1cJZ39s6B1Svg,11344
22
- fakesnow-0.9.25.dist-info/METADATA,sha256=1RktYqC8KfU4ekh8IGApjHSLb5oe3KMgBLjyKJKXHRc,18074
23
- fakesnow-0.9.25.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
24
- fakesnow-0.9.25.dist-info/entry_points.txt,sha256=2riAUgu928ZIHawtO8EsfrMEJhi-EH-z_Vq7Q44xKPM,47
25
- fakesnow-0.9.25.dist-info/top_level.txt,sha256=500evXI1IFX9so82cizGIEMHAb_dJNPaZvd2H9dcKTA,24
26
- fakesnow-0.9.25.dist-info/RECORD,,
21
+ fakesnow-0.9.27.dist-info/LICENSE,sha256=kW-7NWIyaRMQiDpryfSmF2DObDZHGR1cJZ39s6B1Svg,11344
22
+ fakesnow-0.9.27.dist-info/METADATA,sha256=k7PwECuxM55u-Dx_WyieDaH1p2BrHlcmu9INMsxGtzQ,18108
23
+ fakesnow-0.9.27.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
24
+ fakesnow-0.9.27.dist-info/entry_points.txt,sha256=2riAUgu928ZIHawtO8EsfrMEJhi-EH-z_Vq7Q44xKPM,47
25
+ fakesnow-0.9.27.dist-info/top_level.txt,sha256=500evXI1IFX9so82cizGIEMHAb_dJNPaZvd2H9dcKTA,24
26
+ fakesnow-0.9.27.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.44.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5