-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Implements basic snowflake session variables via SET/UNSET (#111)
Implements the very basic usages of sql variables ``` SET var2 = 'blue'; SELECT * from table where color = $var2; Produces: SELECT * from table where color = 'blue'; UNSET var2; ``` as described in https://docs.snowflake.com/en/sql-reference/session-variables. List of features supported and not supported by this PR: - [x] Variables are scoped to the session (Eg. The connection, not the cursor) - [x] Simple scalar variables: SET var1 = 1; - [x] Unset variables: UNSET var1; - [x] Simple SQL expression variables: SET INCREMENTAL_DATE = DATEADD( 'DAY', -7, CURRENT_DATE()); - [x] Basic use of variables in SQL using $ syntax: SELECT $var1; - [ ] Multiple variables: SET (var1, var2) = (1, 'hello'); - [ ] Variables set via 'properties' on the connection https://docs.snowflake.com/en/sql-reference/session-variables#setting-variables-on-connection - [ ] Using variables via the IDENTIFIER function: INSERT INTO IDENTIFIER($my_table_name) (i) VALUES (42); - [ ] Session variable functions: https://docs.snowflake.com/en/sql-reference/session-variables#session-variable-functions
- Loading branch information
1 parent
7656ab9
commit 7696cbd
Showing
4 changed files
with
123 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import re | ||
|
||
import snowflake.connector.errors | ||
from sqlglot import exp | ||
|
||
|
||
# Implements snowflake variables: https://docs.snowflake.com/en/sql-reference/session-variables#using-variables-in-sql | ||
class Variables: | ||
@classmethod | ||
def is_variable_modifier(cls, expr: exp.Expression) -> bool: | ||
return isinstance(expr, exp.Set) or cls._is_unset_expression(expr) | ||
|
||
@classmethod | ||
def _is_unset_expression(cls, expr: exp.Expression) -> bool: | ||
if isinstance(expr, exp.Alias): | ||
this_expr = expr.this.args.get("this") | ||
return isinstance(this_expr, exp.Expression) and this_expr.this == "UNSET" | ||
return False | ||
|
||
def __init__(self) -> None: | ||
self._variables = {} | ||
|
||
def update_variables(self, expr: exp.Expression) -> None: | ||
if isinstance(expr, exp.Set): | ||
unset = expr.args.get("unset") | ||
if not unset: # SET varname = value; | ||
unset_expressions = expr.args.get("expressions") | ||
assert unset_expressions, "SET without values in expression(s) is unexpected." | ||
eq = unset_expressions[0].this | ||
name = eq.this.sql() | ||
value = eq.args.get("expression").sql() | ||
self._set(name, value) | ||
else: | ||
# Haven't been able to produce this in tests yet due to UNSET being parsed as an Alias expression. | ||
raise NotImplementedError("UNSET not supported yet") | ||
elif self._is_unset_expression(expr): # Unfortunately UNSET varname; is parsed as an Alias expression :( | ||
alias = expr.args.get("alias") | ||
assert alias, "UNSET without value in alias attribute is unexpected." | ||
name = alias.this | ||
self._unset(name) | ||
|
||
def _set(self, name: str, value: str) -> None: | ||
self._variables[name] = value | ||
|
||
def _unset(self, name: str) -> None: | ||
self._variables.pop(name) | ||
|
||
def inline_variables(self, sql: str) -> str: | ||
for name, value in self._variables.items(): | ||
sql = re.sub(rf"\${name}", value, sql, flags=re.IGNORECASE) | ||
|
||
remaining_variables = re.search(r"\$\w+", sql) | ||
if remaining_variables: | ||
raise snowflake.connector.errors.ProgrammingError( | ||
msg=f"Session variable '{remaining_variables.group().upper()}' does not exist" | ||
) | ||
return sql |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters