fakesnow 0.9.25__py3-none-any.whl → 0.9.26__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
@@ -37,7 +37,7 @@ def alias_in_join(expression: exp.Expression) -> exp.Expression:
37
37
  def alter_table_strip_cluster_by(expression: exp.Expression) -> exp.Expression:
38
38
  """Turn alter table cluster by into a no-op"""
39
39
  if (
40
- isinstance(expression, exp.AlterTable)
40
+ isinstance(expression, exp.Alter)
41
41
  and (actions := expression.args.get("actions"))
42
42
  and len(actions) == 1
43
43
  and (isinstance(actions[0], exp.Cluster))
@@ -356,7 +356,7 @@ def extract_comment_on_columns(expression: exp.Expression) -> exp.Expression:
356
356
  exp.Expression: The transformed expression, with any comment stored in the new 'table_comment' arg.
357
357
  """
358
358
 
359
- if isinstance(expression, exp.AlterTable) and (actions := expression.args.get("actions")):
359
+ if isinstance(expression, exp.Alter) and (actions := expression.args.get("actions")):
360
360
  new_actions: list[exp.Expression] = []
361
361
  col_comments: list[tuple[str, str]] = []
362
362
  for a in actions:
@@ -410,7 +410,7 @@ def extract_comment_on_table(expression: exp.Expression) -> exp.Expression:
410
410
  new.args["table_comment"] = (table, cexp.this)
411
411
  return new
412
412
  elif (
413
- isinstance(expression, exp.AlterTable)
413
+ isinstance(expression, exp.Alter)
414
414
  and (sexp := expression.find(exp.AlterSet))
415
415
  and (scp := sexp.find(exp.SchemaCommentProperty))
416
416
  and isinstance(scp.this, exp.Literal)
@@ -436,7 +436,7 @@ def extract_text_length(expression: exp.Expression) -> exp.Expression:
436
436
  exp.Expression: The original expression, with any text lengths stored in the new 'text_lengths' arg.
437
437
  """
438
438
 
439
- if isinstance(expression, (exp.Create, exp.AlterTable)):
439
+ if isinstance(expression, (exp.Create, exp.Alter)):
440
440
  text_lengths = []
441
441
 
442
442
  # exp.Select is for a ctas, exp.Schema is a plain definition
@@ -471,7 +471,6 @@ def flatten(expression: exp.Expression) -> exp.Expression:
471
471
 
472
472
  See https://docs.snowflake.com/en/sql-reference/functions/flatten
473
473
 
474
- TODO: return index.
475
474
  TODO: support objects.
476
475
  """
477
476
  if (
@@ -483,20 +482,34 @@ def flatten(expression: exp.Expression) -> exp.Expression:
483
482
  ):
484
483
  explode_expression = expression.this.this.expression
485
484
 
486
- return exp.Lateral(
487
- this=exp.Unnest(
485
+ value = exp.Cast(
486
+ this=explode_expression,
487
+ to=exp.DataType(
488
+ this=exp.DataType.Type.ARRAY,
489
+ expressions=[exp.DataType(this=exp.DataType.Type.JSON, nested=False, prefix=False)],
490
+ nested=True,
491
+ ),
492
+ )
493
+
494
+ return exp.Subquery(
495
+ this=exp.Select(
488
496
  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,
497
+ exp.Unnest(
498
+ expressions=[value],
499
+ alias=exp.Identifier(this="VALUE", quoted=False),
500
+ ),
501
+ exp.Alias(
502
+ this=exp.Sub(
503
+ this=exp.Anonymous(
504
+ this="generate_subscripts", expressions=[value, exp.Literal(this="1", is_string=False)]
505
+ ),
506
+ expression=exp.Literal(this="1", is_string=False),
495
507
  ),
496
- )
508
+ alias=exp.Identifier(this="INDEX", quoted=False),
509
+ ),
497
510
  ],
498
511
  ),
499
- alias=exp.TableAlias(this=alias.this, columns=[exp.Identifier(this="VALUE", quoted=False)]),
512
+ alias=exp.TableAlias(this=alias.this),
500
513
  )
501
514
 
502
515
  return expression
@@ -622,6 +635,20 @@ def information_schema_fs_tables_ext(expression: exp.Expression) -> exp.Expressi
622
635
  return expression
623
636
 
624
637
 
638
+ def information_schema_fs_views(expression: exp.Expression) -> exp.Expression:
639
+ """Use information_schema._fs_views to return Snowflake's version instead of duckdb's."""
640
+
641
+ if (
642
+ isinstance(expression, exp.Select)
643
+ and (tbl_exp := expression.find(exp.Table))
644
+ and tbl_exp.name.upper() == "VIEWS"
645
+ and tbl_exp.db.upper() == "INFORMATION_SCHEMA"
646
+ ):
647
+ tbl_exp.set("this", exp.Identifier(this="_FS_VIEWS", quoted=False))
648
+
649
+ return expression
650
+
651
+
625
652
  def integer_precision(expression: exp.Expression) -> exp.Expression:
626
653
  """Convert integers to bigint.
627
654
 
@@ -707,8 +734,8 @@ def random(expression: exp.Expression) -> exp.Expression:
707
734
  new_rand = exp.Cast(
708
735
  this=exp.Paren(
709
736
  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),
737
+ this=exp.Paren(this=exp.Sub(this=exp.Rand(), expression=exp.Literal(this="0.5", is_string=False))),
738
+ expression=exp.Literal(this="9223372036854775807", is_string=False),
712
739
  )
713
740
  ),
714
741
  to=exp.DataType(this=exp.DataType.Type.BIGINT, nested=False, prefix=False),
@@ -809,31 +836,24 @@ def regex_substr(expression: exp.Expression) -> exp.Expression:
809
836
  pattern.args["this"] = pattern.this.replace("\\\\", "\\")
810
837
 
811
838
  # 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)
839
+ position = expression.args["position"] or exp.Literal(this="1", is_string=False)
816
840
 
817
841
  # which occurrence of the pattern to match
818
- try:
819
- occurrence = int(expression.args["occurrence"].this)
820
- except KeyError:
821
- occurrence = 1
842
+ occurrence = expression.args["occurrence"]
843
+ occurrence = int(occurrence.this) if occurrence else 1
822
844
 
823
845
  # the duckdb dialect increments bracket (ie: index) expressions by 1 because duckdb is 1-indexed,
824
846
  # so we need to compensate by subtracting 1
825
847
  occurrence = exp.Literal(this=str(occurrence - 1), is_string=False)
826
848
 
827
- try:
828
- regex_parameters_value = str(expression.args["parameters"].this)
849
+ if parameters := expression.args["parameters"]:
829
850
  # '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:
851
+ regex_parameters = exp.Literal(this=parameters.this.replace("e", ""), is_string=True)
852
+ else:
832
853
  regex_parameters = exp.Literal(is_string=True)
833
854
 
834
- try:
835
- group_num = expression.args["group"]
836
- except KeyError:
855
+ group_num = expression.args["group"]
856
+ if not group_num:
837
857
  if isinstance(regex_parameters.this, str) and "e" in regex_parameters.this:
838
858
  group_num = exp.Literal(this="1", is_string=False)
839
859
  else:
@@ -1023,7 +1043,7 @@ def tag(expression: exp.Expression) -> exp.Expression:
1023
1043
  exp.Expression: The transformed expression.
1024
1044
  """
1025
1045
 
1026
- if isinstance(expression, exp.AlterTable) and (actions := expression.args.get("actions")):
1046
+ if isinstance(expression, exp.Alter) and (actions := expression.args.get("actions")):
1027
1047
  for a in actions:
1028
1048
  if isinstance(a, exp.AlterSet) and a.args.get("tag"):
1029
1049
  return SUCCESS_NOP
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fakesnow
3
- Version: 0.9.25
3
+ Version: 0.9.26
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,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.24.1
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"
226
+ Requires-Dist: ruff~=0.7.2; extra == "dev"
227
227
  Requires-Dist: twine~=5.0; extra == "dev"
228
- Requires-Dist: snowflake-sqlalchemy~=1.5.0; extra == "dev"
228
+ Requires-Dist: snowflake-sqlalchemy~=1.6.1; 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=VFLA5Fc1i4FuiVdvUuDrK-kA2caqiT8Gw9btMDPJhRA,55367
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.26.dist-info/LICENSE,sha256=kW-7NWIyaRMQiDpryfSmF2DObDZHGR1cJZ39s6B1Svg,11344
22
+ fakesnow-0.9.26.dist-info/METADATA,sha256=92zIwzq7FP-BrfhUcKbdbqYs0eqN9TCKvT_NVdEKZTI,18075
23
+ fakesnow-0.9.26.dist-info/WHEEL,sha256=bFJAMchF8aTQGUgMZzHJyDDMPTO3ToJ7x23SLJa1SVo,92
24
+ fakesnow-0.9.26.dist-info/entry_points.txt,sha256=2riAUgu928ZIHawtO8EsfrMEJhi-EH-z_Vq7Q44xKPM,47
25
+ fakesnow-0.9.26.dist-info/top_level.txt,sha256=500evXI1IFX9so82cizGIEMHAb_dJNPaZvd2H9dcKTA,24
26
+ fakesnow-0.9.26.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.44.0)
2
+ Generator: bdist_wheel (0.45.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5