Repository: cloudspannerecosystem/memefish
Pull request #295 (2025-03-22 09:37:16)

Support ALTER TABLE SET INTERLEAVE IN
This PR implements `ALTER TABLE SET INTERLEAVE IN`.

## Related Issues

- part of #293 


Patch:
diff --git a/ast/ast.go b/ast/ast.go
index dcc95ca..4cbf191 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -437,6 +437,7 @@ func (DropConstraint) isTableAlteration()           {}
 func (DropRowDeletionPolicy) isTableAlteration()    {}
 func (ReplaceRowDeletionPolicy) isTableAlteration() {}
 func (SetOnDelete) isTableAlteration()              {}
+func (SetInterleaveIn) isTableAlteration()          {}
 func (AlterColumn) isTableAlteration()              {}
 func (AlterTableSetOptions) isTableAlteration()     {}
 
@@ -2921,6 +2922,21 @@ type SetOnDelete struct {
 	OnDelete OnDeleteAction
 }
 
+// SetInterleaveIn is SET INTERLEAVE IN clause in ALTER TABLE.
+//
+//	SET INTERLEAVE IN {{if .Enforced}}PARENT{{end}} {{.TableName | sql}} {{.OnDelete}}
+type SetInterleaveIn struct {
+	// pos = Set
+	// end = OnDeleteEnd || TableName.end
+
+	Set         token.Pos // position of "SET" keyword
+	OnDeleteEnd token.Pos // end position of ON DELETE clause
+
+	TableName *Path
+	Enforced  bool
+	OnDelete  OnDeleteAction // optional
+}
+
 // AlterTableSetOptions is SET OPTIONS node in ALTER TABLE.
 //
 //	SET {{.Options | sql}}
diff --git a/ast/pos.go b/ast/pos.go
index 7d29f70..9583b57 100644
--- a/ast/pos.go
+++ b/ast/pos.go
@@ -1390,6 +1390,14 @@ func (s *SetOnDelete) End() token.Pos {
 	return s.OnDeleteEnd
 }
 
+func (s *SetInterleaveIn) Pos() token.Pos {
+	return s.Set
+}
+
+func (s *SetInterleaveIn) End() token.Pos {
+	return posChoice(s.OnDeleteEnd, nodeEnd(wrapNode(s.TableName)))
+}
+
 func (a *AlterTableSetOptions) Pos() token.Pos {
 	return a.Set
 }
diff --git a/ast/sql.go b/ast/sql.go
index 18b5232..ea21bbb 100644
--- a/ast/sql.go
+++ b/ast/sql.go
@@ -948,6 +948,11 @@ func (s *SetOnDelete) SQL() string {
 	return "SET " + string(s.OnDelete)
 }
 
+func (s *SetInterleaveIn) SQL() string {
+	return "SET INTERLEAVE IN " + strOpt(s.Enforced, "PARENT ") + s.TableName.SQL() +
+		strOpt(s.OnDelete != "", " "+string(s.OnDelete))
+}
+
 func (a *AlterTableSetOptions) SQL() string { return "SET " + a.Options.SQL() }
 
 func (a *AlterColumn) SQL() string {
diff --git a/ast/walk_internal.go b/ast/walk_internal.go
index 05ff365..ee60650 100644
--- a/ast/walk_internal.go
+++ b/ast/walk_internal.go
@@ -634,6 +634,9 @@ func walkInternal(node Node, v Visitor, stack []*stackItem) []*stackItem {
 	case *SetOnDelete:
 		// nothing to do
 
+	case *SetInterleaveIn:
+		stack = append(stack, &stackItem{node: wrapNode(n.TableName), visitor: v.Field("TableName")})
+
 	case *AlterTableSetOptions:
 		stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")})
 
diff --git a/parser.go b/parser.go
index 68d99c6..7f6e31c 100644
--- a/parser.go
+++ b/parser.go
@@ -4118,6 +4118,26 @@ func (p *Parser) parseAlterTableSet() ast.TableAlteration {
 			Set:     pos,
 			Options: options,
 		}
+	case p.Token.IsKeywordLike("INTERLEAVE"):
+		p.nextToken()
+		p.expect("IN")
+
+		var enforced bool
+		if p.Token.IsKeywordLike("PARENT") {
+			p.nextToken()
+			enforced = true
+		}
+
+		tableName := p.parsePath()
+		onDelete, onDeleteEnd := p.tryParseOnDeleteAction()
+
+		return &ast.SetInterleaveIn{
+			Set:         pos,
+			Enforced:    enforced,
+			TableName:   tableName,
+			OnDeleteEnd: onDeleteEnd,
+			OnDelete:    onDelete,
+		}
 	default:
 		panic(p.errorfAtToken(&p.Token, "expected token: ON, OPTIONS, but: %s", p.Token.AsString))
 	}


Test patch:
diff --git a/testdata/input/ddl/alter_table_set_interleave_in.sql b/testdata/input/ddl/alter_table_set_interleave_in.sql
new file mode 100644
index 0000000..76893cb
--- /dev/null
+++ b/testdata/input/ddl/alter_table_set_interleave_in.sql
@@ -0,0 +1,1 @@
+alter table foo set interleave in bar
\ No newline at end of file
diff --git a/testdata/input/ddl/alter_table_set_interleave_in_parent.sql b/testdata/input/ddl/alter_table_set_interleave_in_parent.sql
new file mode 100644
index 0000000..4850dad
--- /dev/null
+++ b/testdata/input/ddl/alter_table_set_interleave_in_parent.sql
@@ -0,0 +1,1 @@
+alter table foo set interleave in parent bar
\ No newline at end of file
diff --git a/testdata/input/ddl/alter_table_set_interleave_in_parent_on_delete_cascade.sql b/testdata/input/ddl/alter_table_set_interleave_in_parent_on_delete_cascade.sql
new file mode 100644
index 0000000..a7aebd6
--- /dev/null
+++ b/testdata/input/ddl/alter_table_set_interleave_in_parent_on_delete_cascade.sql
@@ -0,0 +1,1 @@
+alter table foo set interleave in parent bar on delete cascade
\ No newline at end of file
diff --git a/testdata/input/ddl/alter_table_set_interleave_in_parent_on_delete_no_action.sql b/testdata/input/ddl/alter_table_set_interleave_in_parent_on_delete_no_action.sql
new file mode 100644
index 0000000..dd57e5f
--- /dev/null
+++ b/testdata/input/ddl/alter_table_set_interleave_in_parent_on_delete_no_action.sql
@@ -0,0 +1,1 @@
+alter table foo set interleave in parent bar on delete no action
\ No newline at end of file
diff --git a/testdata/input/ddl/named_schema_alter_table_set_interleave_in.sql b/testdata/input/ddl/named_schema_alter_table_set_interleave_in.sql
new file mode 100644
index 0000000..b1e5201
--- /dev/null
+++ b/testdata/input/ddl/named_schema_alter_table_set_interleave_in.sql
@@ -0,0 +1,1 @@
+alter table sch1.foo set interleave in sch1.bar
\ No newline at end of file
diff --git a/testdata/result/ddl/alter_table_set_interleave_in.sql.txt b/testdata/result/ddl/alter_table_set_interleave_in.sql.txt
new file mode 100644
index 0000000..68829b6
--- /dev/null
+++ b/testdata/result/ddl/alter_table_set_interleave_in.sql.txt
@@ -0,0 +1,30 @@
+--- alter_table_set_interleave_in.sql
+alter table foo set interleave in bar
+--- AST
+&ast.AlterTable{
+  Name: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  TableAlteration: &ast.SetInterleaveIn{
+    Set:         16,
+    OnDeleteEnd: -1,
+    TableName:   &ast.Path{
+      Idents: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 34,
+          NameEnd: 37,
+          Name:    "bar",
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+ALTER TABLE foo SET INTERLEAVE IN bar
diff --git a/testdata/result/ddl/alter_table_set_interleave_in_parent.sql.txt b/testdata/result/ddl/alter_table_set_interleave_in_parent.sql.txt
new file mode 100644
index 0000000..ba57c21
--- /dev/null
+++ b/testdata/result/ddl/alter_table_set_interleave_in_parent.sql.txt
@@ -0,0 +1,31 @@
+--- alter_table_set_interleave_in_parent.sql
+alter table foo set interleave in parent bar
+--- AST
+&ast.AlterTable{
+  Name: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  TableAlteration: &ast.SetInterleaveIn{
+    Set:         16,
+    OnDeleteEnd: -1,
+    TableName:   &ast.Path{
+      Idents: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 41,
+          NameEnd: 44,
+          Name:    "bar",
+        },
+      },
+    },
+    Enforced: true,
+  },
+}
+
+--- SQL
+ALTER TABLE foo SET INTERLEAVE IN PARENT bar
diff --git a/testdata/result/ddl/alter_table_set_interleave_in_parent_on_delete_cascade.sql.txt b/testdata/result/ddl/alter_table_set_interleave_in_parent_on_delete_cascade.sql.txt
new file mode 100644
index 0000000..04fb381
--- /dev/null
+++ b/testdata/result/ddl/alter_table_set_interleave_in_parent_on_delete_cascade.sql.txt
@@ -0,0 +1,32 @@
+--- alter_table_set_interleave_in_parent_on_delete_cascade.sql
+alter table foo set interleave in parent bar on delete cascade
+--- AST
+&ast.AlterTable{
+  Name: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  TableAlteration: &ast.SetInterleaveIn{
+    Set:         16,
+    OnDeleteEnd: 62,
+    TableName:   &ast.Path{
+      Idents: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 41,
+          NameEnd: 44,
+          Name:    "bar",
+        },
+      },
+    },
+    Enforced: true,
+    OnDelete: "ON DELETE CASCADE",
+  },
+}
+
+--- SQL
+ALTER TABLE foo SET INTERLEAVE IN PARENT bar ON DELETE CASCADE
diff --git a/testdata/result/ddl/alter_table_set_interleave_in_parent_on_delete_no_action.sql.txt b/testdata/result/ddl/alter_table_set_interleave_in_parent_on_delete_no_action.sql.txt
new file mode 100644
index 0000000..620df53
--- /dev/null
+++ b/testdata/result/ddl/alter_table_set_interleave_in_parent_on_delete_no_action.sql.txt
@@ -0,0 +1,32 @@
+--- alter_table_set_interleave_in_parent_on_delete_no_action.sql
+alter table foo set interleave in parent bar on delete no action
+--- AST
+&ast.AlterTable{
+  Name: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  TableAlteration: &ast.SetInterleaveIn{
+    Set:         16,
+    OnDeleteEnd: 64,
+    TableName:   &ast.Path{
+      Idents: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 41,
+          NameEnd: 44,
+          Name:    "bar",
+        },
+      },
+    },
+    Enforced: true,
+    OnDelete: "ON DELETE NO ACTION",
+  },
+}
+
+--- SQL
+ALTER TABLE foo SET INTERLEAVE IN PARENT bar ON DELETE NO ACTION
diff --git a/testdata/result/ddl/named_schema_alter_table_set_interleave_in.sql.txt b/testdata/result/ddl/named_schema_alter_table_set_interleave_in.sql.txt
new file mode 100644
index 0000000..379abfa
--- /dev/null
+++ b/testdata/result/ddl/named_schema_alter_table_set_interleave_in.sql.txt
@@ -0,0 +1,40 @@
+--- named_schema_alter_table_set_interleave_in.sql
+alter table sch1.foo set interleave in sch1.bar
+--- AST
+&ast.AlterTable{
+  Name: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 16,
+        Name:    "sch1",
+      },
+      &ast.Ident{
+        NamePos: 17,
+        NameEnd: 20,
+        Name:    "foo",
+      },
+    },
+  },
+  TableAlteration: &ast.SetInterleaveIn{
+    Set:         21,
+    OnDeleteEnd: -1,
+    TableName:   &ast.Path{
+      Idents: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 39,
+          NameEnd: 43,
+          Name:    "sch1",
+        },
+        &ast.Ident{
+          NamePos: 44,
+          NameEnd: 47,
+          Name:    "bar",
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+ALTER TABLE sch1.foo SET INTERLEAVE IN sch1.bar
diff --git a/testdata/result/statement/alter_table_set_interleave_in.sql.txt b/testdata/result/statement/alter_table_set_interleave_in.sql.txt
new file mode 100644
index 0000000..68829b6
--- /dev/null
+++ b/testdata/result/statement/alter_table_set_interleave_in.sql.txt
@@ -0,0 +1,30 @@
+--- alter_table_set_interleave_in.sql
+alter table foo set interleave in bar
+--- AST
+&ast.AlterTable{
+  Name: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  TableAlteration: &ast.SetInterleaveIn{
+    Set:         16,
+    OnDeleteEnd: -1,
+    TableName:   &ast.Path{
+      Idents: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 34,
+          NameEnd: 37,
+          Name:    "bar",
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+ALTER TABLE foo SET INTERLEAVE IN bar
diff --git a/testdata/result/statement/alter_table_set_interleave_in_parent.sql.txt b/testdata/result/statement/alter_table_set_interleave_in_parent.sql.txt
new file mode 100644
index 0000000..ba57c21
--- /dev/null
+++ b/testdata/result/statement/alter_table_set_interleave_in_parent.sql.txt
@@ -0,0 +1,31 @@
+--- alter_table_set_interleave_in_parent.sql
+alter table foo set interleave in parent bar
+--- AST
+&ast.AlterTable{
+  Name: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  TableAlteration: &ast.SetInterleaveIn{
+    Set:         16,
+    OnDeleteEnd: -1,
+    TableName:   &ast.Path{
+      Idents: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 41,
+          NameEnd: 44,
+          Name:    "bar",
+        },
+      },
+    },
+    Enforced: true,
+  },
+}
+
+--- SQL
+ALTER TABLE foo SET INTERLEAVE IN PARENT bar
diff --git a/testdata/result/statement/alter_table_set_interleave_in_parent_on_delete_cascade.sql.txt b/testdata/result/statement/alter_table_set_interleave_in_parent_on_delete_cascade.sql.txt
new file mode 100644
index 0000000..04fb381
--- /dev/null
+++ b/testdata/result/statement/alter_table_set_interleave_in_parent_on_delete_cascade.sql.txt
@@ -0,0 +1,32 @@
+--- alter_table_set_interleave_in_parent_on_delete_cascade.sql
+alter table foo set interleave in parent bar on delete cascade
+--- AST
+&ast.AlterTable{
+  Name: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  TableAlteration: &ast.SetInterleaveIn{
+    Set:         16,
+    OnDeleteEnd: 62,
+    TableName:   &ast.Path{
+      Idents: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 41,
+          NameEnd: 44,
+          Name:    "bar",
+        },
+      },
+    },
+    Enforced: true,
+    OnDelete: "ON DELETE CASCADE",
+  },
+}
+
+--- SQL
+ALTER TABLE foo SET INTERLEAVE IN PARENT bar ON DELETE CASCADE
diff --git a/testdata/result/statement/alter_table_set_interleave_in_parent_on_delete_no_action.sql.txt b/testdata/result/statement/alter_table_set_interleave_in_parent_on_delete_no_action.sql.txt
new file mode 100644
index 0000000..620df53
--- /dev/null
+++ b/testdata/result/statement/alter_table_set_interleave_in_parent_on_delete_no_action.sql.txt
@@ -0,0 +1,32 @@
+--- alter_table_set_interleave_in_parent_on_delete_no_action.sql
+alter table foo set interleave in parent bar on delete no action
+--- AST
+&ast.AlterTable{
+  Name: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 15,
+        Name:    "foo",
+      },
+    },
+  },
+  TableAlteration: &ast.SetInterleaveIn{
+    Set:         16,
+    OnDeleteEnd: 64,
+    TableName:   &ast.Path{
+      Idents: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 41,
+          NameEnd: 44,
+          Name:    "bar",
+        },
+      },
+    },
+    Enforced: true,
+    OnDelete: "ON DELETE NO ACTION",
+  },
+}
+
+--- SQL
+ALTER TABLE foo SET INTERLEAVE IN PARENT bar ON DELETE NO ACTION
diff --git a/testdata/result/statement/named_schema_alter_table_set_interleave_in.sql.txt b/testdata/result/statement/named_schema_alter_table_set_interleave_in.sql.txt
new file mode 100644
index 0000000..379abfa
--- /dev/null
+++ b/testdata/result/statement/named_schema_alter_table_set_interleave_in.sql.txt
@@ -0,0 +1,40 @@
+--- named_schema_alter_table_set_interleave_in.sql
+alter table sch1.foo set interleave in sch1.bar
+--- AST
+&ast.AlterTable{
+  Name: &ast.Path{
+    Idents: []*ast.Ident{
+      &ast.Ident{
+        NamePos: 12,
+        NameEnd: 16,
+        Name:    "sch1",
+      },
+      &ast.Ident{
+        NamePos: 17,
+        NameEnd: 20,
+        Name:    "foo",
+      },
+    },
+  },
+  TableAlteration: &ast.SetInterleaveIn{
+    Set:         21,
+    OnDeleteEnd: -1,
+    TableName:   &ast.Path{
+      Idents: []*ast.Ident{
+        &ast.Ident{
+          NamePos: 39,
+          NameEnd: 43,
+          Name:    "sch1",
+        },
+        &ast.Ident{
+          NamePos: 44,
+          NameEnd: 47,
+          Name:    "bar",
+        },
+      },
+    },
+  },
+}
+
+--- SQL
+ALTER TABLE sch1.foo SET INTERLEAVE IN sch1.bar