Skip to content

Commit 4423059

Browse files
ca0904Chetan Agrawaltekumara
authored
feat: Add support for multi-column ALTER TABLE ADD COLUMN statements (#247)
# feat: Add support for multi-column ALTER TABLE ADD COLUMN statements ## Overview This PR introduces support for Snowflake's multi-column `ALTER TABLE ADD COLUMN` syntax, allowing users to add multiple columns in a single statement. The implementation automatically splits these statements into individual DuckDB-compatible `ALTER TABLE` commands. Resolves #244 Co-authored-by: Chetan Agrawal <chetan.agrawal@moveworks.ai> Co-authored-by: Oliver Mannion <125105+tekumara@users.noreply.github.com>
1 parent 2529fef commit 4423059

File tree

5 files changed

+95
-2
lines changed

5 files changed

+95
-2
lines changed

fakesnow/cursor.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,19 @@ def _transform(self, expression: exp.Expression, params: MutableParams | None) -
317317
def _transform_explode(self, expression: exp.Expression) -> list[exp.Expression]:
318318
# Applies transformations that require splitting the expression into multiple expressions
319319
# Split transforms have limited support at the moment.
320-
return transforms.merge(expression)
320+
321+
# Try merge transform first
322+
merge_result = transforms.merge(expression)
323+
if len(merge_result) > 1:
324+
return merge_result
325+
326+
# If merge didn't split the expression, try alter_table_add_multiple_columns
327+
alter_result = transforms.alter_table_add_multiple_columns(expression)
328+
if len(alter_result) > 1:
329+
return alter_result
330+
331+
# Return original expression if no transform applied
332+
return [expression]
321333

322334
def _execute(self, transformed: exp.Expression, params: MutableParams | None = None) -> None:
323335
self._arrow_table = None

fakesnow/transforms/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
from __future__ import annotations
22

3+
from fakesnow.transforms.ddl import (
4+
alter_table_add_multiple_columns as alter_table_add_multiple_columns,
5+
)
36
from fakesnow.transforms.merge import merge as merge
47
from fakesnow.transforms.show import (
58
show_columns as show_columns,

fakesnow/transforms/ddl.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""DDL (Data Definition Language) transforms for fakesnow.
2+
3+
This module contains transformations for DDL statements like ALTER TABLE, CREATE TABLE, etc.
4+
5+
Future DDL transforms can be added here:
6+
- CREATE TABLE enhancements
7+
- DROP TABLE transformations
8+
- ALTER TABLE modifications (beyond ADD COLUMN)
9+
- INDEX operations
10+
- VIEW operations
11+
- etc.
12+
"""
13+
14+
from __future__ import annotations
15+
16+
from sqlglot import exp
17+
18+
19+
def alter_table_add_multiple_columns(expression: exp.Expression) -> list[exp.Expression]:
20+
"""Transform ALTER TABLE ADD COLUMN with multiple columns into separate statements.
21+
22+
Snowflake supports: ALTER TABLE tab1 ADD COLUMN col1 INT, col2 VARCHAR(50), col3 BOOLEAN;
23+
DuckDB requires separate statements for each column.
24+
25+
Args:
26+
expression: The expression to potentially transform
27+
28+
Returns:
29+
List of expressions - multiple ALTER statements if transformation applied,
30+
otherwise single original expression
31+
"""
32+
if not isinstance(expression, exp.Alter):
33+
return [expression]
34+
35+
actions = expression.args.get("actions")
36+
if not (
37+
actions
38+
and isinstance(actions, list)
39+
and all(isinstance(action, exp.ColumnDef) for action in actions)
40+
and len(actions) > 1
41+
):
42+
return [expression]
43+
44+
# Create separate ALTER statements for each column
45+
alter_statements = []
46+
for action in actions:
47+
new_alter = exp.Alter(
48+
this=expression.this,
49+
kind=expression.args.get("kind"),
50+
exists=expression.args.get("exists"),
51+
actions=[action],
52+
)
53+
alter_statements.append(new_alter)
54+
55+
return alter_statements

tests/test_ddl.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import sqlglot
2+
3+
from fakesnow.transforms.ddl import alter_table_add_multiple_columns
4+
5+
6+
def test_alter_table_add_multiple_columns() -> None:
7+
# Test multiple columns are split into separate statements
8+
sql = "alter table tab1 add column col1 int, col2 varchar(50), col3 boolean"
9+
result = alter_table_add_multiple_columns(sqlglot.parse_one(sql, dialect="snowflake"))
10+
11+
assert len(result) == 3
12+
assert result[0].sql() == "ALTER TABLE tab1 ADD COLUMN col1 INT"
13+
assert result[1].sql() == "ALTER TABLE tab1 ADD COLUMN col2 VARCHAR(50)"
14+
assert result[2].sql() == "ALTER TABLE tab1 ADD COLUMN col3 BOOLEAN"
15+
16+
# Test multiple columns with IF EXISTS clause
17+
sql_if_exists = "alter table if exists tab1 add column col1 int, col2 varchar(50)"
18+
result_if_exists = alter_table_add_multiple_columns(sqlglot.parse_one(sql_if_exists, dialect="snowflake"))
19+
20+
assert len(result_if_exists) == 2
21+
assert result_if_exists[0].sql() == "ALTER TABLE IF EXISTS tab1 ADD COLUMN col1 INT"
22+
assert result_if_exists[1].sql() == "ALTER TABLE IF EXISTS tab1 ADD COLUMN col2 VARCHAR(50)"

tests/test_fakes.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ def test_alias_on_join(conn: snowflake.connector.SnowflakeConnection):
3939
def test_alter_table(dcur: snowflake.connector.cursor.SnowflakeCursor):
4040
dcur.execute("create table table1 (id int)")
4141
dcur.execute("alter table table1 add column name varchar(20)")
42-
dcur.execute("select name from table1")
42+
dcur.execute("alter table if exists table1 add column col1 int, col2 varchar(50)")
43+
dcur.execute("select id,name,col1,col2 from table1")
4344
assert dcur.execute("alter table table1 cluster by (name)").fetchall() == [
4445
{"status": "Statement executed successfully."}
4546
]

0 commit comments

Comments
 (0)