Skip to content

Commit

Permalink
fix: Support Spanner ROW DELETION POLICY
Browse files Browse the repository at this point in the history
  • Loading branch information
ginokent committed Jun 6, 2024
1 parent 834c015 commit e2ee48b
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 9 deletions.
2 changes: 1 addition & 1 deletion pkg/ddl/spanner/ddl_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ func (o *Option) String() string {
}

func (o *Option) StringForDiff() string {
if o.Value == nil {
if o == nil || o.Value == nil {
return ""
}
return o.Name + " " + o.Value.StringForDiff()
Expand Down
31 changes: 31 additions & 0 deletions pkg/ddl/spanner/ddl_table_alter.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ func (s *AlterTableStmt) String() string {
} else {
str += " INITIALLY IMMEDIATE"
}
case *AddRowDeletionPolicy:
str += "ADD " + a.RowDeletionPolicy.String()
case *ReplaceRowDeletionPolicy:
str += "REPLACE " + a.RowDeletionPolicy.String()
case *DropRowDeletionPolicy:
str += "DROP ROW DELETION POLICY"
}

return str + ";\n"
Expand Down Expand Up @@ -217,3 +223,28 @@ type AlterConstraint struct {
func (*AlterConstraint) isAlterTableAction() {}

func (s *AlterConstraint) GoString() string { return internal.GoString(*s) }

// AddRowDeletionPolicy represents ALTER TABLE table_name ADD ROW DELETION POLICY.
type AddRowDeletionPolicy struct {
RowDeletionPolicy *Option
}

func (*AddRowDeletionPolicy) isAlterTableAction() {}

func (s *AddRowDeletionPolicy) GoString() string { return internal.GoString(*s) }

// ReplaceRowDeletionPolicy represents ALTER TABLE table_name REPLACE ROW DELETION POLICY.
type ReplaceRowDeletionPolicy struct {
RowDeletionPolicy *Option
}

func (*ReplaceRowDeletionPolicy) isAlterTableAction() {}

func (s *ReplaceRowDeletionPolicy) GoString() string { return internal.GoString(*s) }

// DropRowDeletionPolicy represents ALTER TABLE table_name DROP ROW DELETION POLICY.
type DropRowDeletionPolicy struct{}

func (*DropRowDeletionPolicy) isAlterTableAction() {}

func (s *DropRowDeletionPolicy) GoString() string { return internal.GoString(*s) }
76 changes: 76 additions & 0 deletions pkg/ddl/spanner/ddl_table_alter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ func Test_isAlterTableAction(t *testing.T) {
(&AddConstraint{}).isAlterTableAction()
(&DropConstraint{}).isAlterTableAction()
(&AlterConstraint{}).isAlterTableAction()
(&AddRowDeletionPolicy{}).isAlterTableAction()
(&ReplaceRowDeletionPolicy{}).isAlterTableAction()
(&DropRowDeletionPolicy{}).isAlterTableAction()
}

func TestAlterTableStmt_String(t *testing.T) {
Expand Down Expand Up @@ -266,6 +269,79 @@ func TestAlterTableStmt_String(t *testing.T) {
}
t.Logf("✅: %s: stmt: %#v", t.Name(), stmt)
})

t.Run("success,AddRowDeletionPolicy", func(t *testing.T) {
t.Parallel()

stmt := &AlterTableStmt{
Name: &ObjectName{Name: &Ident{Name: "groups", QuotationMark: `"`, Raw: `"groups"`}},
Action: &AddRowDeletionPolicy{RowDeletionPolicy: &Option{Name: "ROW DELETION POLICY", Value: &Expr{Idents: []*Ident{
NewRawIdent("("),
NewRawIdent("OLDER_THAN"),
NewRawIdent("("),
NewRawIdent("CreatedAt"),
NewRawIdent(","),
NewRawIdent("INTERVAL"),
NewRawIdent("30"),
NewRawIdent("DAY"),
NewRawIdent(")"),
NewRawIdent(")"),
}}}},
}

expected := `ALTER TABLE "groups" ADD ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 30 DAY));` + "\n"
actual := stmt.String()

if !assert.Equal(t, expected, actual) {
assert.Equal(t, fmt.Sprintf("%#v", expected), fmt.Sprintf("%#v", actual))
}
t.Logf("✅: %s: stmt: %#v", t.Name(), stmt)
})

t.Run("success,ReplaceRowDeletionPolicy", func(t *testing.T) {
t.Parallel()

stmt := &AlterTableStmt{
Name: &ObjectName{Name: &Ident{Name: "groups", QuotationMark: `"`, Raw: `"groups"`}},
Action: &ReplaceRowDeletionPolicy{RowDeletionPolicy: &Option{Name: "ROW DELETION POLICY", Value: &Expr{Idents: []*Ident{
NewRawIdent("("),
NewRawIdent("OLDER_THAN"),
NewRawIdent("("),
NewRawIdent("CreatedAt"),
NewRawIdent(","),
NewRawIdent("INTERVAL"),
NewRawIdent("30"),
NewRawIdent("DAY"),
NewRawIdent(")"),
NewRawIdent(")"),
}}}},
}

expected := `ALTER TABLE "groups" REPLACE ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 30 DAY));` + "\n"
actual := stmt.String()

if !assert.Equal(t, expected, actual) {
assert.Equal(t, fmt.Sprintf("%#v", expected), fmt.Sprintf("%#v", actual))
}
t.Logf("✅: %s: stmt: %#v", t.Name(), stmt)
})

t.Run("success,DropRowDeletionPolicy", func(t *testing.T) {
t.Parallel()

stmt := &AlterTableStmt{
Name: &ObjectName{Name: &Ident{Name: "groups", QuotationMark: `"`, Raw: `"groups"`}},
Action: &DropRowDeletionPolicy{},
}

expected := `ALTER TABLE "groups" DROP ROW DELETION POLICY;` + "\n"
actual := stmt.String()

if !assert.Equal(t, expected, actual) {
assert.Equal(t, fmt.Sprintf("%#v", expected), fmt.Sprintf("%#v", actual))
}
t.Logf("✅: %s: stmt: %#v", t.Name(), stmt)
})
}

func TestAlterTableStmt_GetNameForDiff(t *testing.T) {
Expand Down
18 changes: 11 additions & 7 deletions pkg/ddl/spanner/ddl_table_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import (
var _ Stmt = (*CreateTableStmt)(nil)

type CreateTableStmt struct {
Comment string
Indent string
IfNotExists bool
Name *ObjectName
Columns []*Column
Constraints Constraints
Options Options
Comment string
Indent string
IfNotExists bool
Name *ObjectName
Columns []*Column
Constraints Constraints
Options Options
RowDeletionPolicy *Option
}

func (s *CreateTableStmt) GetNameForDiff() string {
Expand Down Expand Up @@ -74,6 +75,9 @@ func (s *CreateTableStmt) String() string {
}
}
}
if s.RowDeletionPolicy != nil {
str += ",\n" + s.RowDeletionPolicy.String()
}

str += ";\n"
return str
Expand Down
30 changes: 30 additions & 0 deletions pkg/ddl/spanner/diff_create_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,36 @@ func DiffCreateTable(before, after *CreateTableStmt, opts ...DiffCreateTableOpti
})
}

if before.RowDeletionPolicy.StringForDiff() != after.RowDeletionPolicy.StringForDiff() {
switch {
case before.RowDeletionPolicy == nil:
// ALTER TABLE table_name ADD ROW DELETION POLICY
result.Stmts = append(result.Stmts, &AlterTableStmt{
Comment: simplediff.Diff("", after.RowDeletionPolicy.String()).String(),
Name: after.Name,
Action: &AddRowDeletionPolicy{
RowDeletionPolicy: after.RowDeletionPolicy,
},
})
case after.RowDeletionPolicy == nil:
// ALTER TABLE table_name DROP ROW DELETION POLICY
result.Stmts = append(result.Stmts, &AlterTableStmt{
Comment: simplediff.Diff(before.RowDeletionPolicy.String(), "").String(),
Name: after.Name,
Action: &DropRowDeletionPolicy{},
})
default:
// ALTER TABLE table_name REPLACE ROW DELETION POLICY
result.Stmts = append(result.Stmts, &AlterTableStmt{
Comment: simplediff.Diff(before.RowDeletionPolicy.String(), after.RowDeletionPolicy.String()).String(),
Name: after.Name,
Action: &ReplaceRowDeletionPolicy{
RowDeletionPolicy: after.RowDeletionPolicy,
},
})
}
}

if len(result.Stmts) == 0 {
return nil, apperr.Errorf("before: %s, after: %s: %w", before.GetNameForDiff(), after.GetNameForDiff(), ddl.ErrNoDifference)
}
Expand Down
75 changes: 75 additions & 0 deletions pkg/ddl/spanner/diff_create_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,81 @@ CREATE TABLE "users" (
assert.Equal(t, expected, actual.String())
})

t.Run("success,ALTER_ADD_ROW_DELETION_POLICY", func(t *testing.T) {
t.Parallel()

before := `CREATE TABLE "users" (id STRING(36) NOT NULL, group_id STRING(36) NOT NULL REFERENCES "groups" ("id"), "name" STRING(255) NOT NULL, "age" INT64 DEFAULT 0 NOT NULL CHECK ("age" >= 0), description STRING, CreatedAt TIMESTAMP) PRIMARY KEY ("id");`
beforeDDL, err := NewParser(NewLexer(before)).Parse()
require.NoError(t, err)

after := `CREATE TABLE "users" (id STRING(36) NOT NULL, group_id STRING(36) NOT NULL REFERENCES "groups" ("id"), "name" STRING(255) NOT NULL, "age" INT64 DEFAULT 0 NOT NULL CHECK ("age" >= 0), description STRING, CreatedAt TIMESTAMP) PRIMARY KEY ("id"), ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 7 DAY));`
afterDDL, err := NewParser(NewLexer(after)).Parse()
require.NoError(t, err)

actual, err := DiffCreateTable(
beforeDDL.Stmts[0].(*CreateTableStmt),
afterDDL.Stmts[0].(*CreateTableStmt),
DiffCreateTableUseAlterTableAddConstraintNotValid(false),
)
assert.NoError(t, err)
expected := `-- -
-- +ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 7 DAY))
ALTER TABLE "users" ADD ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 7 DAY));
`

assert.Equal(t, expected, actual.String())
})

t.Run("success,ALTER_REPLACE_ROW_DELETION_POLICY", func(t *testing.T) {
t.Parallel()

before := `CREATE TABLE "users" (id STRING(36) NOT NULL, group_id STRING(36) NOT NULL REFERENCES "groups" ("id"), "name" STRING(255) NOT NULL, "age" INT64 DEFAULT 0 NOT NULL CHECK ("age" >= 0), description STRING, CreatedAt TIMESTAMP) PRIMARY KEY ("id"), ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 30 DAY));`
beforeDDL, err := NewParser(NewLexer(before)).Parse()
require.NoError(t, err)

after := `CREATE TABLE "users" (id STRING(36) NOT NULL, group_id STRING(36) NOT NULL REFERENCES "groups" ("id"), "name" STRING(255) NOT NULL, "age" INT64 DEFAULT 0 NOT NULL CHECK ("age" >= 0), description STRING, CreatedAt TIMESTAMP) PRIMARY KEY ("id"), ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 7 DAY));`
afterDDL, err := NewParser(NewLexer(after)).Parse()
require.NoError(t, err)

actual, err := DiffCreateTable(
beforeDDL.Stmts[0].(*CreateTableStmt),
afterDDL.Stmts[0].(*CreateTableStmt),
DiffCreateTableUseAlterTableAddConstraintNotValid(false),
)
assert.NoError(t, err)
expected := `-- -ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 30 DAY))
-- +ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 7 DAY))
ALTER TABLE "users" REPLACE ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 7 DAY));
`

assert.Equal(t, expected, actual.String())
})

t.Run("success,ALTER_REPLACE_DROP_DELETION_POLICY", func(t *testing.T) {
t.Parallel()

before := `CREATE TABLE "users" (id STRING(36) NOT NULL, group_id STRING(36) NOT NULL REFERENCES "groups" ("id"), "name" STRING(255) NOT NULL, "age" INT64 DEFAULT 0 NOT NULL CHECK ("age" >= 0), description STRING, CreatedAt TIMESTAMP) PRIMARY KEY ("id"), ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 30 DAY));`
beforeDDL, err := NewParser(NewLexer(before)).Parse()
require.NoError(t, err)

after := `CREATE TABLE "users" (id STRING(36) NOT NULL, group_id STRING(36) NOT NULL REFERENCES "groups" ("id"), "name" STRING(255) NOT NULL, "age" INT64 DEFAULT 0 NOT NULL CHECK ("age" >= 0), description STRING, CreatedAt TIMESTAMP) PRIMARY KEY ("id");`
afterDDL, err := NewParser(NewLexer(after)).Parse()
require.NoError(t, err)

actual, err := DiffCreateTable(
beforeDDL.Stmts[0].(*CreateTableStmt),
afterDDL.Stmts[0].(*CreateTableStmt),
DiffCreateTableUseAlterTableAddConstraintNotValid(false),
)
assert.NoError(t, err)
expected := `-- -ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 30 DAY))
-- +
ALTER TABLE "users" DROP ROW DELETION POLICY;
`

assert.Equal(t, expected, actual.String())
})

t.Run("success,DROP_ADD_FOREIGN_KEY", func(t *testing.T) {
t.Parallel()

Expand Down
2 changes: 1 addition & 1 deletion pkg/ddl/spanner/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ LabelTableOptions:
//
opt.Value = opt.Value.Append(rowDeletionPolicyContent...)

createTableStmt.Options = append(createTableStmt.Options, opt)
createTableStmt.RowDeletionPolicy = opt
case TOKEN_COMMA:
// do nothing
case TOKEN_SEMICOLON, TOKEN_EOF:
Expand Down

0 comments on commit e2ee48b

Please sign in to comment.