fakesnow 0.9.18__py3-none-any.whl → 0.9.19__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 CHANGED
@@ -33,6 +33,7 @@ import fakesnow.info_schema as info_schema
33
33
  import fakesnow.macros as macros
34
34
  import fakesnow.transforms as transforms
35
35
  from fakesnow.global_database import create_global_database
36
+ from fakesnow.variables import Variables
36
37
 
37
38
  SCHEMA_UNSET = "schema_unset"
38
39
  SQL_SUCCESS = "SELECT 'Statement executed successfully.' as 'status'"
@@ -134,6 +135,7 @@ class FakeSnowflakeCursor:
134
135
  if os.environ.get("FAKESNOW_DEBUG") == "snowflake":
135
136
  print(f"{command};{params=}" if params else f"{command};", file=sys.stderr)
136
137
 
138
+ command = self._inline_variables(command)
137
139
  command, params = self._rewrite_with_params(command, params)
138
140
  if self._conn.nop_regexes and any(re.match(p, command, re.IGNORECASE) for p in self._conn.nop_regexes):
139
141
  transformed = transforms.SUCCESS_NOP
@@ -148,6 +150,7 @@ class FakeSnowflakeCursor:
148
150
  def _transform(self, expression: exp.Expression) -> exp.Expression:
149
151
  return (
150
152
  expression.transform(transforms.upper_case_unquoted_identifiers)
153
+ .transform(transforms.update_variables, variables=self._conn.variables)
151
154
  .transform(transforms.set_schema, current_database=self._conn.database)
152
155
  .transform(transforms.create_database, db_path=self._conn.db_path)
153
156
  .transform(transforms.extract_comment_on_table)
@@ -501,6 +504,9 @@ class FakeSnowflakeCursor:
501
504
 
502
505
  return command, params
503
506
 
507
+ def _inline_variables(self, sql: str) -> str:
508
+ return self._conn.variables.inline_variables(sql)
509
+
504
510
 
505
511
  class FakeSnowflakeConnection:
506
512
  def __init__(
@@ -525,6 +531,7 @@ class FakeSnowflakeConnection:
525
531
  self.db_path = Path(db_path) if db_path else None
526
532
  self.nop_regexes = nop_regexes
527
533
  self._paramstyle = snowflake.connector.paramstyle
534
+ self.variables = Variables()
528
535
 
529
536
  create_global_database(duck_conn)
530
537
 
fakesnow/transforms.py CHANGED
@@ -8,6 +8,7 @@ import sqlglot
8
8
  from sqlglot import exp
9
9
 
10
10
  from fakesnow.global_database import USERS_TABLE_FQ_NAME
11
+ from fakesnow.variables import Variables
11
12
 
12
13
  MISSING_DATABASE = "missing_database"
13
14
  SUCCESS_NOP = sqlglot.parse_one("SELECT 'Statement executed successfully.'")
@@ -1407,6 +1408,16 @@ def show_keys(
1407
1408
  return expression
1408
1409
 
1409
1410
 
1411
+ def update_variables(
1412
+ expression: exp.Expression,
1413
+ variables: Variables,
1414
+ ) -> exp.Expression:
1415
+ if Variables.is_variable_modifier(expression):
1416
+ variables.update_variables(expression)
1417
+ return SUCCESS_NOP # Nothing further to do if its a SET/UNSET operation.
1418
+ return expression
1419
+
1420
+
1410
1421
  class SHA256(exp.Func):
1411
1422
  _sql_names: ClassVar = ["SHA256"]
1412
1423
  arg_types: ClassVar = {"this": True}
fakesnow/variables.py ADDED
@@ -0,0 +1,57 @@
1
+ import re
2
+
3
+ import snowflake.connector.errors
4
+ from sqlglot import exp
5
+
6
+
7
+ # Implements snowflake variables: https://docs.snowflake.com/en/sql-reference/session-variables#using-variables-in-sql
8
+ class Variables:
9
+ @classmethod
10
+ def is_variable_modifier(cls, expr: exp.Expression) -> bool:
11
+ return isinstance(expr, exp.Set) or cls._is_unset_expression(expr)
12
+
13
+ @classmethod
14
+ def _is_unset_expression(cls, expr: exp.Expression) -> bool:
15
+ if isinstance(expr, exp.Alias):
16
+ this_expr = expr.this.args.get("this")
17
+ return isinstance(this_expr, exp.Expression) and this_expr.this == "UNSET"
18
+ return False
19
+
20
+ def __init__(self) -> None:
21
+ self._variables = {}
22
+
23
+ def update_variables(self, expr: exp.Expression) -> None:
24
+ if isinstance(expr, exp.Set):
25
+ unset = expr.args.get("unset")
26
+ if not unset: # SET varname = value;
27
+ unset_expressions = expr.args.get("expressions")
28
+ assert unset_expressions, "SET without values in expression(s) is unexpected."
29
+ eq = unset_expressions[0].this
30
+ name = eq.this.sql()
31
+ value = eq.args.get("expression").sql()
32
+ self._set(name, value)
33
+ else:
34
+ # Haven't been able to produce this in tests yet due to UNSET being parsed as an Alias expression.
35
+ raise NotImplementedError("UNSET not supported yet")
36
+ elif self._is_unset_expression(expr): # Unfortunately UNSET varname; is parsed as an Alias expression :(
37
+ alias = expr.args.get("alias")
38
+ assert alias, "UNSET without value in alias attribute is unexpected."
39
+ name = alias.this
40
+ self._unset(name)
41
+
42
+ def _set(self, name: str, value: str) -> None:
43
+ self._variables[name] = value
44
+
45
+ def _unset(self, name: str) -> None:
46
+ self._variables.pop(name)
47
+
48
+ def inline_variables(self, sql: str) -> str:
49
+ for name, value in self._variables.items():
50
+ sql = re.sub(rf"\${name}", value, sql, flags=re.IGNORECASE)
51
+
52
+ remaining_variables = re.search(r"\$\w+", sql)
53
+ if remaining_variables:
54
+ raise snowflake.connector.errors.ProgrammingError(
55
+ msg=f"Session variable '{remaining_variables.group().upper()}' does not exist"
56
+ )
57
+ return sql
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fakesnow
3
- Version: 0.9.18
3
+ Version: 0.9.19
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
@@ -3,16 +3,17 @@ 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=u2EDQ_fXQFFx5bq5pgLrUXV33Flgg5U82L7LIvFiqGw,30556
6
+ fakesnow/fakes.py,sha256=cTPjOaFZJbyLOJgh7yGRKxUVXM_MYQbBj9NiK2bkhSs,30881
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=1xok38tft_VowwSm8kKA3P3OEZEeGF09hqfpawxXNaY,52648
13
- fakesnow-0.9.18.dist-info/LICENSE,sha256=kW-7NWIyaRMQiDpryfSmF2DObDZHGR1cJZ39s6B1Svg,11344
14
- fakesnow-0.9.18.dist-info/METADATA,sha256=cmS6VqDOHlkSZgfhT7biB03DuSktq4DNtv3EYbFDrrg,17839
15
- fakesnow-0.9.18.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
16
- fakesnow-0.9.18.dist-info/entry_points.txt,sha256=2riAUgu928ZIHawtO8EsfrMEJhi-EH-z_Vq7Q44xKPM,47
17
- fakesnow-0.9.18.dist-info/top_level.txt,sha256=500evXI1IFX9so82cizGIEMHAb_dJNPaZvd2H9dcKTA,24
18
- fakesnow-0.9.18.dist-info/RECORD,,
12
+ fakesnow/transforms.py,sha256=7L7hWg98wrscsIV2-BrTaEhJHhSc9mtroxy-SJkpCnA,52994
13
+ fakesnow/variables.py,sha256=I7kVVlqttJ_XlYK4fZ-WTSJgGVdvq_DLX3GZR6QoxN8,2361
14
+ fakesnow-0.9.19.dist-info/LICENSE,sha256=kW-7NWIyaRMQiDpryfSmF2DObDZHGR1cJZ39s6B1Svg,11344
15
+ fakesnow-0.9.19.dist-info/METADATA,sha256=p4tC5MVPyi6Ud33RZaTBHeYwbYu-Xwu7vNvtWafvkHk,17839
16
+ fakesnow-0.9.19.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
17
+ fakesnow-0.9.19.dist-info/entry_points.txt,sha256=2riAUgu928ZIHawtO8EsfrMEJhi-EH-z_Vq7Q44xKPM,47
18
+ fakesnow-0.9.19.dist-info/top_level.txt,sha256=500evXI1IFX9so82cizGIEMHAb_dJNPaZvd2H9dcKTA,24
19
+ fakesnow-0.9.19.dist-info/RECORD,,