fakesnow 0.9.8__tar.gz → 0.9.10__tar.gz

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.
Files changed (32) hide show
  1. {fakesnow-0.9.8 → fakesnow-0.9.10}/PKG-INFO +3 -3
  2. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow/fakes.py +1 -0
  3. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow/transforms.py +35 -5
  4. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow.egg-info/PKG-INFO +3 -3
  5. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow.egg-info/requires.txt +2 -2
  6. {fakesnow-0.9.8 → fakesnow-0.9.10}/pyproject.toml +3 -3
  7. {fakesnow-0.9.8 → fakesnow-0.9.10}/tests/test_fakes.py +17 -0
  8. {fakesnow-0.9.8 → fakesnow-0.9.10}/tests/test_transforms.py +27 -0
  9. {fakesnow-0.9.8 → fakesnow-0.9.10}/LICENSE +0 -0
  10. {fakesnow-0.9.8 → fakesnow-0.9.10}/README.md +0 -0
  11. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow/__init__.py +0 -0
  12. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow/__main__.py +0 -0
  13. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow/checks.py +0 -0
  14. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow/cli.py +0 -0
  15. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow/expr.py +0 -0
  16. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow/fixtures.py +0 -0
  17. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow/global_database.py +0 -0
  18. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow/info_schema.py +0 -0
  19. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow/macros.py +0 -0
  20. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow/py.typed +0 -0
  21. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow.egg-info/SOURCES.txt +0 -0
  22. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow.egg-info/dependency_links.txt +0 -0
  23. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow.egg-info/entry_points.txt +0 -0
  24. {fakesnow-0.9.8 → fakesnow-0.9.10}/fakesnow.egg-info/top_level.txt +0 -0
  25. {fakesnow-0.9.8 → fakesnow-0.9.10}/setup.cfg +0 -0
  26. {fakesnow-0.9.8 → fakesnow-0.9.10}/tests/test_checks.py +0 -0
  27. {fakesnow-0.9.8 → fakesnow-0.9.10}/tests/test_cli.py +0 -0
  28. {fakesnow-0.9.8 → fakesnow-0.9.10}/tests/test_expr.py +0 -0
  29. {fakesnow-0.9.8 → fakesnow-0.9.10}/tests/test_info_schema.py +0 -0
  30. {fakesnow-0.9.8 → fakesnow-0.9.10}/tests/test_patch.py +0 -0
  31. {fakesnow-0.9.8 → fakesnow-0.9.10}/tests/test_sqlalchemy.py +0 -0
  32. {fakesnow-0.9.8 → fakesnow-0.9.10}/tests/test_users.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fakesnow
3
- Version: 0.9.8
3
+ Version: 0.9.10
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,14 +213,14 @@ 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~=23.3.0
216
+ Requires-Dist: sqlglot~=23.12.2
217
217
  Provides-Extra: dev
218
218
  Requires-Dist: build~=1.0; extra == "dev"
219
219
  Requires-Dist: pandas-stubs; extra == "dev"
220
220
  Requires-Dist: snowflake-connector-python[pandas,secure-local-storage]; extra == "dev"
221
221
  Requires-Dist: pre-commit~=3.4; extra == "dev"
222
222
  Requires-Dist: pytest~=8.0; extra == "dev"
223
- Requires-Dist: ruff~=0.3.2; extra == "dev"
223
+ Requires-Dist: ruff~=0.4.2; extra == "dev"
224
224
  Requires-Dist: twine~=5.0; extra == "dev"
225
225
  Requires-Dist: snowflake-sqlalchemy~=1.5.0; extra == "dev"
226
226
  Provides-Extra: notebook
@@ -193,6 +193,7 @@ class FakeSnowflakeCursor:
193
193
  .transform(transforms.show_users)
194
194
  .transform(transforms.create_user)
195
195
  .transform(transforms.sha256)
196
+ .transform(transforms.create_clone)
196
197
  )
197
198
 
198
199
  def _execute(
@@ -55,6 +55,27 @@ def array_agg_within_group(expression: exp.Expression) -> exp.Expression:
55
55
  return expression
56
56
 
57
57
 
58
+ def create_clone(expression: exp.Expression) -> exp.Expression:
59
+ """Transform create table clone to create table as select."""
60
+
61
+ if (
62
+ isinstance(expression, exp.Create)
63
+ and str(expression.args.get("kind")).upper() == "TABLE"
64
+ and (clone := expression.find(exp.Clone))
65
+ ):
66
+ return exp.Create(
67
+ this=expression.this,
68
+ kind="TABLE",
69
+ expression=exp.Select(
70
+ expressions=[
71
+ exp.Star(),
72
+ ],
73
+ **{"from": exp.From(this=clone.this)},
74
+ ),
75
+ )
76
+ return expression
77
+
78
+
58
79
  # TODO: move this into a Dialect as a transpilation
59
80
  def create_database(expression: exp.Expression, db_path: Path | None = None) -> exp.Expression:
60
81
  """Transform create database to attach database.
@@ -309,7 +330,7 @@ def extract_comment_on_table(expression: exp.Expression) -> exp.Expression:
309
330
  if props := cast(exp.Properties, expression.args.get("properties")):
310
331
  other_props = []
311
332
  for p in props.expressions:
312
- if isinstance(p, exp.SchemaCommentProperty) and (isinstance(p.this, (exp.Literal, exp.Identifier))):
333
+ if isinstance(p, exp.SchemaCommentProperty) and (isinstance(p.this, (exp.Literal, exp.Var))):
313
334
  comment = p.this.this
314
335
  else:
315
336
  other_props.append(p)
@@ -360,10 +381,19 @@ def extract_text_length(expression: exp.Expression) -> exp.Expression:
360
381
 
361
382
  if isinstance(expression, (exp.Create, exp.AlterTable)):
362
383
  text_lengths = []
363
- for dt in expression.find_all(exp.DataType):
364
- if dt.this in (exp.DataType.Type.VARCHAR, exp.DataType.Type.TEXT):
365
- col_name = dt.parent and dt.parent.this and dt.parent.this.this
366
- if dt_size := dt.find(exp.DataTypeParam):
384
+
385
+ # exp.Select is for a ctas, exp.Schema is a plain definition
386
+ if cols := expression.find(exp.Select, exp.Schema):
387
+ expressions = cols.expressions
388
+ else:
389
+ # alter table
390
+ expressions = expression.args.get("actions") or []
391
+ for e in expressions:
392
+ if dts := [
393
+ dt for dt in e.find_all(exp.DataType) if dt.this in (exp.DataType.Type.VARCHAR, exp.DataType.Type.TEXT)
394
+ ]:
395
+ col_name = e.alias if isinstance(e, exp.Alias) else e.name
396
+ if len(dts) == 1 and (dt_size := dts[0].find(exp.DataTypeParam)):
367
397
  size = (
368
398
  isinstance(dt_size.this, exp.Literal)
369
399
  and isinstance(dt_size.this.this, str)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fakesnow
3
- Version: 0.9.8
3
+ Version: 0.9.10
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,14 +213,14 @@ 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~=23.3.0
216
+ Requires-Dist: sqlglot~=23.12.2
217
217
  Provides-Extra: dev
218
218
  Requires-Dist: build~=1.0; extra == "dev"
219
219
  Requires-Dist: pandas-stubs; extra == "dev"
220
220
  Requires-Dist: snowflake-connector-python[pandas,secure-local-storage]; extra == "dev"
221
221
  Requires-Dist: pre-commit~=3.4; extra == "dev"
222
222
  Requires-Dist: pytest~=8.0; extra == "dev"
223
- Requires-Dist: ruff~=0.3.2; extra == "dev"
223
+ Requires-Dist: ruff~=0.4.2; extra == "dev"
224
224
  Requires-Dist: twine~=5.0; extra == "dev"
225
225
  Requires-Dist: snowflake-sqlalchemy~=1.5.0; extra == "dev"
226
226
  Provides-Extra: notebook
@@ -1,7 +1,7 @@
1
1
  duckdb~=0.10.0
2
2
  pyarrow
3
3
  snowflake-connector-python
4
- sqlglot~=23.3.0
4
+ sqlglot~=23.12.2
5
5
 
6
6
  [dev]
7
7
  build~=1.0
@@ -9,7 +9,7 @@ pandas-stubs
9
9
  snowflake-connector-python[pandas,secure-local-storage]
10
10
  pre-commit~=3.4
11
11
  pytest~=8.0
12
- ruff~=0.3.2
12
+ ruff~=0.4.2
13
13
  twine~=5.0
14
14
  snowflake-sqlalchemy~=1.5.0
15
15
 
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "fakesnow"
3
3
  description = "Fake Snowflake Connector for Python. Run, mock and test Snowflake DB locally."
4
- version = "0.9.8"
4
+ version = "0.9.10"
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
7
7
  classifiers = ["License :: OSI Approved :: MIT License"]
@@ -11,7 +11,7 @@ dependencies = [
11
11
  "duckdb~=0.10.0",
12
12
  "pyarrow",
13
13
  "snowflake-connector-python",
14
- "sqlglot~=23.3.0",
14
+ "sqlglot~=23.12.2",
15
15
  ]
16
16
 
17
17
  [project.urls]
@@ -29,7 +29,7 @@ dev = [
29
29
  "snowflake-connector-python[pandas, secure-local-storage]",
30
30
  "pre-commit~=3.4",
31
31
  "pytest~=8.0",
32
- "ruff~=0.3.2",
32
+ "ruff~=0.4.2",
33
33
  "twine~=5.0",
34
34
  "snowflake-sqlalchemy~=1.5.0",
35
35
  ]
@@ -110,6 +110,16 @@ def test_binding_qmark(_fakesnow: None):
110
110
  cur.execute("select * from customers where id = ?", (1,))
111
111
 
112
112
 
113
+ def test_clone(cur: snowflake.connector.cursor.SnowflakeCursor):
114
+ cur.execute("create table customers (ID int, FIRST_NAME varchar, ACTIVE boolean)")
115
+ cur.execute("insert into customers values (1, 'Jenny', True)")
116
+
117
+ cur.execute("create table customers2 clone db1.schema1.customers")
118
+ cur.execute("select * from customers2")
119
+ # TODO check tags are copied too
120
+ assert cur.fetchall() == [(1, "Jenny", True)]
121
+
122
+
113
123
  def test_close_conn(conn: snowflake.connector.SnowflakeConnection, cur: snowflake.connector.cursor.SnowflakeCursor):
114
124
  conn.close()
115
125
  with pytest.raises(snowflake.connector.errors.DatabaseError) as excinfo:
@@ -1284,6 +1294,13 @@ def test_show_primary_keys(dcur: snowflake.connector.cursor.SnowflakeCursor):
1284
1294
  assert result3 == []
1285
1295
 
1286
1296
 
1297
+ def test_sqlglot_regression(cur: snowflake.connector.cursor.SnowflakeCursor):
1298
+ assert cur.execute(
1299
+ """with SOURCE_TABLE AS (SELECT '2024-01-01' AS start_date)
1300
+ SELECT date(a.start_date) from SOURCE_TABLE AS a"""
1301
+ ).fetchone() == (datetime.date(2024, 1, 1),)
1302
+
1303
+
1287
1304
  def test_sqlstate(cur: snowflake.connector.cursor.SnowflakeCursor):
1288
1305
  cur.execute("select 'hello world'")
1289
1306
  # sqlstate is None on success
@@ -9,6 +9,7 @@ from fakesnow.transforms import (
9
9
  _get_to_number_args,
10
10
  array_agg_within_group,
11
11
  array_size,
12
+ create_clone,
12
13
  create_database,
13
14
  dateadd_date_cast,
14
15
  dateadd_string_literal_timestamp_cast,
@@ -84,6 +85,13 @@ def test_array_agg_within_group() -> None:
84
85
  )
85
86
 
86
87
 
88
+ def test_create_clone() -> None:
89
+ assert (
90
+ sqlglot.parse_one("create table customers2 clone db1.schema1.customer").transform(create_clone).sql()
91
+ == "CREATE TABLE customers2 AS SELECT * FROM db1.schema1.customer"
92
+ )
93
+
94
+
87
95
  def test_create_database() -> None:
88
96
  e = sqlglot.parse_one("create database foobar").transform(create_database)
89
97
  assert e.sql() == "ATTACH DATABASE ':memory:' AS foobar"
@@ -335,6 +343,25 @@ def test_extract_text_length() -> None:
335
343
  assert e.sql() == sql
336
344
  assert e.args["text_lengths"] == [("t1", 16777216), ("t2", 10), ("t3", 20)]
337
345
 
346
+ sql = "ALTER TABLE t1 ALTER COLUMN c4 SET DATA TYPE VARCHAR(50)"
347
+ e = sqlglot.parse_one(sql).transform(extract_text_length)
348
+ assert e.sql() == sql
349
+ assert e.args["text_lengths"] == [("c4", 50)]
350
+
351
+ # test column name is correct with alias
352
+ sql = """CREATE TABLE table1 AS (
353
+ SELECT CAST(C1 AS TEXT) AS K, CAST(C2 AS TEXT(10)) AS V
354
+ FROM (VALUES (1, 2)) AS T(C1, C2))"""
355
+ e = sqlglot.parse_one(sql).transform(extract_text_length)
356
+ assert e.args["text_lengths"] == [("K", 16777216), ("V", 10)]
357
+
358
+ # test ctas column name is correct for combined field
359
+ sql = """CREATE TABLE SOME_TABLE AS (
360
+ SELECT CAST(C1 AS TEXT) || '-' || CAST(C1 AS TEXT) AS K
361
+ FROM VALUES (1), (2) AS T (C1))"""
362
+ e = sqlglot.parse_one(sql).transform(extract_text_length)
363
+ assert e.args["text_lengths"] == [("K", 16777216)]
364
+
338
365
 
339
366
  def test_flatten() -> None:
340
367
  assert (
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes