Repository: cloudspannerecosystem/memefish (go)

Issue:
Incompatibility: ALTER COLUMN DROP DEFAULT
This incompatibility is detected by #90.

## Spec

https://cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language#alter_table

```
ALTER TABLE table_name
    action

where action is:
...
    ALTER [ COLUMN ] column_name
      {
        data_type  [ NOT NULL ] [ DEFAULT ( expression ) ]
        | SET OPTIONS ( options_def )
        | SET DEFAULT ( expression )
        | DROP DEFAULT
      }
...
```
## Reproducing input
```
ALTER TABLE T ALTER COLUMN expr DROP DEFAULT;"
```

## Current behavior

```
syntax error:testdata/column_default_values.test:6:33: expect ident: BOOL, INT64, FLOAT32, FLOAT64, DATE, TIMESTAMP, NUMERIC, JSON, STRING, BYTES, but: DROP

  6:  ALTER TABLE T ALTER COLUMN expr DROP DEFAULT;
                                      ^~~~
```

Pull request:
Implement ALTER COLUMN DROP DEFAULT
This PR implements `ALTER COLUMN column_name DROP DEFAULT`.
Also it introduces `ColumnAlteration` interface to simplify `ALTER COLUMN` actions.

fix #92 

Patch:
diff --git a/ast/ast.go b/ast/ast.go
index 1d8c21e..23909c9 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -50,31 +50,36 @@ type Statement interface {
 	isStatement()
 }
 
+// The order of this list follows the official documentation:
+//
+// - https://cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language
+// - https://cloud.google.com/spanner/docs/reference/standard-sql/dml-syntax
+
 func (QueryStatement) isStatement()     {}
 func (CreateDatabase) isStatement()     {}
 func (CreateTable) isStatement()        {}
-func (CreateSequence) isStatement()     {}
+func (AlterTable) isStatement()         {}
+func (DropTable) isStatement()          {}
+func (CreateIndex) isStatement()        {}
+func (AlterIndex) isStatement()         {}
+func (DropIndex) isStatement()          {}
 func (CreateView) isStatement()         {}
 func (DropView) isStatement()           {}
-func (CreateIndex) isStatement()        {}
-func (CreateVectorIndex) isStatement()  {}
+func (CreateChangeStream) isStatement() {}
+func (AlterChangeStream) isStatement()  {}
+func (DropChangeStream) isStatement()   {}
 func (CreateRole) isStatement()         {}
-func (AlterTable) isStatement()         {}
-func (AlterIndex) isStatement()         {}
+func (DropRole) isStatement()           {}
+func (Grant) isStatement()              {}
+func (Revoke) isStatement()             {}
+func (CreateSequence) isStatement()     {}
 func (AlterSequence) isStatement()      {}
-func (DropTable) isStatement()          {}
-func (DropIndex) isStatement()          {}
-func (DropVectorIndex) isStatement()    {}
 func (DropSequence) isStatement()       {}
-func (DropRole) isStatement()           {}
+func (CreateVectorIndex) isStatement()  {}
+func (DropVectorIndex) isStatement()    {}
 func (Insert) isStatement()             {}
 func (Delete) isStatement()             {}
 func (Update) isStatement()             {}
-func (Grant) isStatement()              {}
-func (Revoke) isStatement()             {}
-func (CreateChangeStream) isStatement() {}
-func (AlterChangeStream) isStatement()  {}
-func (DropChangeStream) isStatement()   {}
 
 // QueryExpr represents set operator operands.
 type QueryExpr interface {
@@ -254,27 +259,31 @@ type DDL interface {
 	isDDL()
 }
 
+// The order of this list follows the official documentation:
+//
+// - https://cloud.google.com/spanner/docs/reference/standard-sql/data-definition-language
+
 func (CreateDatabase) isDDL()     {}
 func (CreateTable) isDDL()        {}
-func (CreateView) isDDL()         {}
-func (DropView) isDDL()           {}
-func (CreateSequence) isDDL()     {}
 func (AlterTable) isDDL()         {}
 func (DropTable) isDDL()          {}
 func (CreateIndex) isDDL()        {}
-func (CreateVectorIndex) isDDL()  {}
 func (AlterIndex) isDDL()         {}
-func (AlterSequence) isDDL()      {}
 func (DropIndex) isDDL()          {}
-func (DropVectorIndex) isDDL()    {}
-func (DropSequence) isDDL()       {}
+func (CreateView) isDDL()         {}
+func (DropView) isDDL()           {}
+func (CreateChangeStream) isDDL() {}
+func (AlterChangeStream) isDDL()  {}
+func (DropChangeStream) isDDL()   {}
 func (CreateRole) isDDL()         {}
 func (DropRole) isDDL()           {}
 func (Grant) isDDL()              {}
 func (Revoke) isDDL()             {}
-func (CreateChangeStream) isDDL() {}
-func (AlterChangeStream) isDDL()  {}
-func (DropChangeStream) isDDL()   {}
+func (CreateSequence) isDDL()     {}
+func (AlterSequence) isDDL()      {}
+func (DropSequence) isDDL()       {}
+func (CreateVectorIndex) isDDL()  {}
+func (DropVectorIndex) isDDL()    {}
 
 // Constraint represents table constraint of CONSTARINT clause.
 type Constraint interface {
@@ -300,7 +309,17 @@ func (DropRowDeletionPolicy) isTableAlteration()    {}
 func (ReplaceRowDeletionPolicy) isTableAlteration() {}
 func (SetOnDelete) isTableAlteration()              {}
 func (AlterColumn) isTableAlteration()              {}
-func (AlterColumnSet) isTableAlteration()           {}
+
+// ColumnAlteration represents ALTER COLUMN action in ALTER TABLE.
+type ColumnAlteration interface {
+	Node
+	isColumnAlteration()
+}
+
+func (AlterColumnType) isColumnAlteration()        {}
+func (AlterColumnSetOptions) isColumnAlteration()  {}
+func (AlterColumnSetDefault) isColumnAlteration()  {}
+func (AlterColumnDropDefault) isColumnAlteration() {}
 
 // Privilege represents privileges specified by GRANT and REVOKE.
 type Privilege interface {
@@ -1009,14 +1028,14 @@ type IndexExpr struct {
 
 // CallExpr is function call expression node.
 //
-//		{{.Func | sql}}(
+//	{{.Func | sql}}(
 //		{{if .Distinct}}DISTINCT{{end}}
 //		{{.Args | sqlJoin ", "}}
 //		{{if len(.Args) > 0 && len(.NamedArgs) > 0}}, {{end}}
 //		{{.NamedArgs | sqlJoin ", "}}
-//	    {{.NullHandling | sqlOpt}}
-//	    {{.Having | sqlOpt}}
-//		)
+//		{{.NullHandling | sqlOpt}}
+//		{{.Having | sqlOpt}}
+//	)
 type CallExpr struct {
 	// pos = Func.pos
 	// end = Rparen + 1
@@ -1159,16 +1178,18 @@ type AtTimeZone struct {
 	Expr Expr
 }
 
-// CastExpr is CAST call expression node.
+// CastExpr is CAST/SAFE_CAST call expression node.
 //
-//	CAST({{.Expr | sql}} AS {{.Type | sql}})
+//	{{if .Safe}}SAFE_{{end}}CAST({{.Expr | sql}} AS {{.Type | sql}})
 type CastExpr struct {
 	// pos = Cast
 	// end = Rparen + 1
 
-	Cast   token.Pos // position of "CAST" keyword
+	Cast   token.Pos // position of "CAST" keyword or "SAFE_CAST" pseudo keyword
 	Rparen token.Pos // position of ")"
 
+	Safe bool
+
 	Expr Expr
 	Type Type
 }
@@ -1960,33 +1981,63 @@ type SetOnDelete struct {
 
 // AlterColumn is ALTER COLUMN clause in ALTER TABLE.
 //
-//	ALTER COLUMN {{.Name | sql}} {{.Type | sql}} {{if .NotNull}}NOT NULL{{end}} {{.DefaultExpr | sqlOpt}}
+//	ALTER COLUMN {{.Name | sql}} {{.Alteration | sql}}
 type AlterColumn struct {
 	// pos = Alter
-	// end = DefaultExpr.end || Null + 4 || Type.end
+	// end = Alteration.end
+
 	Alter token.Pos // position of "ALTER" keyword
-	Null  token.Pos // position of "NULL"
 
-	Name        *Ident
+	Name       *Ident
+	Alteration ColumnAlteration
+}
+
+// AlterColumnType is action to change the data type of the column in ALTER COLUMN.
+//
+//	{{.Type | sql}} {{if .NotNull}}NOT NULL{{end}} {{.DefaultExpr | sqlOpt}}
+type AlterColumnType struct {
+	// pos = Type.pos
+	// end = DefaultExpr.end || NUll + 4 || Type.end
+
 	Type        SchemaType
+	Null        token.Pos // position of "NULL" keyword, optional
 	NotNull     bool
-	DefaultExpr *ColumnDefaultExpr
+	DefaultExpr *ColumnDefaultExpr // optional
 }
 
-// AlterColumnSet is ALTER COLUMN SET clause in ALTER TABLE.
+// AlterColumnSetOptions is SET OPTIONS node in ALTER COLUMN.
 //
-//	ALTER COLUMN {{.Name | sql}} SET {{if .Options}}{{.Options | sql}}{{else}}{{.DefaultExpr | sql}}{{end}}
-type AlterColumnSet struct {
-	// pos = Alter
-	// end = Name.end || Options.end || DefaultExpr.end
+//	SET {{.Options | sql}}
+type AlterColumnSetOptions struct {
+	// pos = Set
+	// end = Options.end
 
-	Alter token.Pos // position of "ALTER" keyword
+	Set     token.Pos
+	Options *Options
+}
 
-	Name        *Ident
-	Options     *Options
+// AlterColumnSetDefault is SET DEFAULT node in ALTER COLUMN.
+//
+//	SET {{.DefaultExpr | sql}}
+type AlterColumnSetDefault struct {
+	// pos = Set
+	// end = DefaultExpr.end
+
+	Set         token.Pos
 	DefaultExpr *ColumnDefaultExpr
 }
 
+// AlterColumnDropDefault is DROP DEFAULT node in ALTER COLUMN
+//
+//	DROP DEFAULT
+type AlterColumnDropDefault struct {
+	// pos = Drop
+	// end = Default + 7
+
+	Drop    token.Pos
+	Default token.Pos
+}
+
 // DropTable is DROP TABLE statement node.
 //
 //	DROP TABLE {{if .IfExists}}IF NOT EXISTS{{end}} {{.Name | sql}}
diff --git a/ast/options.go b/ast/options.go
index 931c64c..5c3d698 100644
--- a/ast/options.go
+++ b/ast/options.go
@@ -23,11 +23,11 @@ func (o *Options) Field(name string) (expr Expr, found bool) {
 
 // FieldNotFound is returned Options.*Field methods.
 // It can be handled as a non-error.
-var FieldNotFound = errors.New("field not found")
+var ErrFieldNotFound = errors.New("field not found")
 
 // fieldTypeMismatch is only defined for test.
 // It is intentionally unexported.
-var fieldTypeMismatch = errors.New("type mismatched")
+var errFieldTypeMismatch = errors.New("type mismatched")
 
 // BoolField finds name in records, and return its value as *bool.
 // If Options doesn't have a record with name, it returns FieldNotFound error.
@@ -37,7 +37,7 @@ var fieldTypeMismatch = errors.New("type mismatched")
 func (o *Options) BoolField(name string) (*bool, error) {
 	v, ok := o.Field(name)
 	if !ok {
-		return nil, FieldNotFound
+		return nil, ErrFieldNotFound
 	}
 	switch v := v.(type) {
 	case *BoolLiteral:
@@ -45,7 +45,7 @@ func (o *Options) BoolField(name string) (*bool, error) {
 	case *NullLiteral:
 		return nil, nil
 	default:
-		return nil, fmt.Errorf("expect true, false or null, but got unknown type %T: %w", v, fieldTypeMismatch)
+		return nil, fmt.Errorf("expect true, false or null, but got unknown type %T: %w", v, errFieldTypeMismatch)
 	}
 }
 
@@ -57,7 +57,7 @@ func (o *Options) BoolField(name string) (*bool, error) {
 func (o *Options) IntegerField(name string) (*int64, error) {
 	v, ok := o.Field(name)
 	if !ok {
-		return nil, FieldNotFound
+		return nil, ErrFieldNotFound
 	}
 	switch v := v.(type) {
 	case *IntLiteral:
@@ -69,7 +69,7 @@ func (o *Options) IntegerField(name string) (*int64, error) {
 	case *NullLiteral:
 		return nil, nil
 	default:
-		return nil, fmt.Errorf("expect integer or null, but got unknown type %T: %w", v, fieldTypeMismatch)
+		return nil, fmt.Errorf("expect integer or null, but got unknown type %T: %w", v, errFieldTypeMismatch)
 	}
 }
 
@@ -81,7 +81,7 @@ func (o *Options) IntegerField(name string) (*int64, error) {
 func (o *Options) StringField(name string) (*string, error) {
 	v, ok := o.Field(name)
 	if !ok {
-		return nil, FieldNotFound
+		return nil, ErrFieldNotFound
 	}
 	switch v := v.(type) {
 	case *StringLiteral:
@@ -89,6 +89,6 @@ func (o *Options) StringField(name string) (*string, error) {
 	case *NullLiteral:
 		return nil, nil
 	default:
-		return nil, fmt.Errorf("expect string literal or null, but got unknown type %T: %w", v, fieldTypeMismatch)
+		return nil, fmt.Errorf("expect string literal or null, but got unknown type %T: %w", v, errFieldTypeMismatch)
 	}
 }
diff --git a/ast/pos.go b/ast/pos.go
index 12fe577..161c53f 100644
--- a/ast/pos.go
+++ b/ast/pos.go
@@ -695,23 +695,30 @@ func (a *AlterColumn) Pos() token.Pos {
 }
 
 func (a *AlterColumn) End() token.Pos {
+	return a.Alteration.End()
+}
+
+func (a *AlterColumnType) Pos() token.Pos { return a.Type.Pos() }
+
+func (a *AlterColumnType) End() token.Pos {
+	// TODO: use firstValid
 	if a.DefaultExpr != nil {
 		return a.DefaultExpr.End()
 	}
-	if !a.Null.Invalid() {
+	if a.NotNull {
 		return a.Null + 4
 	}
 	return a.Type.End()
 }
 
-func (a *AlterColumnSet) Pos() token.Pos { return a.Alter }
+func (a *AlterColumnSetOptions) Pos() token.Pos { return a.Set }
+func (a *AlterColumnSetOptions) End() token.Pos { return a.Options.End() }
 
-func (a *AlterColumnSet) End() token.Pos {
-	if a.Options != nil {
-		return a.Options.End()
-	}
-	return a.DefaultExpr.End()
-}
+func (a *AlterColumnSetDefault) Pos() token.Pos { return a.Set }
+func (a *AlterColumnSetDefault) End() token.Pos { return a.DefaultExpr.End() }
+
+func (a *AlterColumnDropDefault) Pos() token.Pos { return a.Drop }
+func (a *AlterColumnDropDefault) End() token.Pos { return a.Default + 7 }
 
 func (d *DropTable) Pos() token.Pos { return d.Drop }
 func (d *DropTable) End() token.Pos { return d.Name.End() }
diff --git a/ast/sql.go b/ast/sql.go
index 34c16ec..45393fb 100644
--- a/ast/sql.go
+++ b/ast/sql.go
@@ -555,7 +555,7 @@ func (a *AtTimeZone) SQL() string {
 }
 
 func (c *CastExpr) SQL() string {
-	return "CAST(" + c.Expr.SQL() + " AS " + c.Type.SQL() + ")"
+	return strOpt(c.Safe, "SAFE_") + "CAST(" + c.Expr.SQL() + " AS " + c.Type.SQL() + ")"
 }
 
 func (c *CaseExpr) SQL() string {
@@ -957,25 +957,21 @@ func (s *SetOnDelete) SQL() string {
 }
 
 func (a *AlterColumn) SQL() string {
-	sql := "ALTER COLUMN " + a.Name.SQL() + " " + a.Type.SQL()
-	if a.NotNull {
-		sql += " NOT NULL"
-	}
-	if a.DefaultExpr != nil {
-		sql += " " + a.DefaultExpr.SQL()
-	}
-	return sql
+	return "ALTER COLUMN " + a.Name.SQL() + " " + a.Alteration.SQL()
 }
 
-func (a *AlterColumnSet) SQL() string {
-	sql := "ALTER COLUMN " + a.Name.SQL() + " SET "
-	if a.Options != nil {
-		return sql + a.Options.SQL()
-	} else {
-		return sql + a.DefaultExpr.SQL()
-	}
+func (a *AlterColumnType) SQL() string {
+	return a.Type.SQL() +
+		strOpt(a.NotNull, " NOT NULL") +
+		sqlOpt(" ", a.DefaultExpr, "")
 }
 
+func (a *AlterColumnSetOptions) SQL() string { return "SET " + a.Options.SQL() }
+
+func (a *AlterColumnSetDefault) SQL() string { return "SET " + a.DefaultExpr.SQL() }
+
+func (a *AlterColumnDropDefault) SQL() string { return "DROP DEFAULT" }
+
 func (d *DropTable) SQL() string {
 	sql := "DROP TABLE "
 	if d.IfExists {
diff --git a/parser.go b/parser.go
index eefa17b..d5d0c98 100644
--- a/parser.go
+++ b/parser.go
@@ -1415,6 +1415,10 @@ func (p *Parser) parseLit() ast.Expr {
 		return p.parseParenExpr()
 	case token.TokenIdent:
 		id := p.Token
+		switch {
+		case id.IsKeywordLike("SAFE_CAST"):
+			return p.parseCastExpr()
+		}
 		p.nextToken()
 		switch p.Token.Kind {
 		case "(":
@@ -1681,7 +1685,19 @@ func (p *Parser) parseCaseElse() *ast.CaseElse {
 }
 
 func (p *Parser) parseCastExpr() *ast.CastExpr {
-	cast := p.expect("CAST").Pos
+	if p.Token.Kind != "CAST" && !p.Token.IsKeywordLike("SAFE_CAST") {
+		panic(p.errorfAtToken(&p.Token, `expected CAST keyword or SAFE_CAST pseudo keyword, but: %v`, p.Token.Kind))
+	}
+
+	var cast token.Pos
+	var safe bool
+	if p.Token.Kind == "CAST" {
+		cast = p.expect("CAST").Pos
+	} else {
+		cast = p.expectKeywordLike("SAFE_CAST").Pos
+		safe = true
+	}
+
 	p.expect("(")
 	e := p.parseExpr()
 	p.expect("AS")
@@ -1690,6 +1706,7 @@ func (p *Parser) parseCastExpr() *ast.CastExpr {
 	return &ast.CastExpr{
 		Cast:   cast,
 		Rparen: rparen,
+		Safe:   safe,
 		Expr:   e,
 		Type:   t,
 	}
@@ -2928,40 +2945,53 @@ func (p *Parser) parseSetOnDelete() *ast.SetOnDelete {
 	}
 }
 
-func (p *Parser) parseAlterColumn() ast.TableAlteration {
-	pos := p.expectKeywordLike("ALTER").Pos
-	p.expectKeywordLike("COLUMN")
-
-	name := p.parseIdent()
-
-	if p.Token.Kind == "SET" {
-		p.nextToken()
+func (p *Parser) parseColumnAlteration() ast.ColumnAlteration {
+	switch {
+	case p.Token.Kind == "SET":
+		set := p.expect("SET").Pos
 		if p.Token.Kind == "DEFAULT" {
 			defaultExpr := p.tryParseColumnDefaultExpr()
-			return &ast.AlterColumnSet{
-				Alter:       pos,
-				Name:        name,
+			return &ast.AlterColumnSetDefault{
+				Set:         set,
 				DefaultExpr: defaultExpr,
 			}
 		} else {
 			options := p.parseOptions()
-			return &ast.AlterColumnSet{
-				Alter:   pos,
-				Name:    name,
+			return &ast.AlterColumnSetOptions{
+				Set:     set,
 				Options: options,
 			}
 		}
+	case p.Token.IsKeywordLike("DROP"):
+		drop := p.expectKeywordLike("DROP").Pos
+		def := p.expect("DEFAULT").Pos
+		return &ast.AlterColumnDropDefault{
+			Drop:    drop,
+			Default: def,
+		}
+	default:
+		t, notNull, null := p.parseTypeNotNull()
+		defaultExpr := p.tryParseColumnDefaultExpr()
+		return &ast.AlterColumnType{
+			Type:        t,
+			Null:        null,
+			NotNull:     notNull,
+			DefaultExpr: defaultExpr,
+		}
 	}
 
-	t, notNull, null := p.parseTypeNotNull()
-	defaultExpr := p.tryParseColumnDefaultExpr()
+}
+func (p *Parser) parseAlterColumn() ast.TableAlteration {
+	pos := p.expectKeywordLike("ALTER").Pos
+	p.expectKeywordLike("COLUMN")
+
+	name := p.parseIdent()
+
+	alteration := p.parseColumnAlteration()
 	return &ast.AlterColumn{
-		Alter:       pos,
-		Null:        null,
-		Name:        name,
-		Type:        t,
-		NotNull:     notNull,
-		DefaultExpr: defaultExpr,
+		Alter:      pos,
+		Name:       name,
+		Alteration: alteration,
 	}
 }
 


Test patch:
diff --git a/ast/ast_test.go b/ast/ast_test.go
index 4c974d3..3232f53 100644
--- a/ast/ast_test.go
+++ b/ast/ast_test.go
@@ -8,21 +8,28 @@ func TestStatement(t *testing.T) {
 	Statement(&QueryStatement{}).isStatement()
 	Statement(&CreateDatabase{}).isStatement()
 	Statement(&CreateTable{}).isStatement()
-	Statement(&CreateView{}).isStatement()
-	Statement(&CreateIndex{}).isStatement()
-	Statement(&CreateSequence{}).isStatement()
-	Statement(&CreateRole{}).isStatement()
 	Statement(&AlterTable{}).isStatement()
-	Statement(&AlterIndex{}).isStatement()
 	Statement(&DropTable{}).isStatement()
+	Statement(&CreateIndex{}).isStatement()
+	Statement(&AlterIndex{}).isStatement()
 	Statement(&DropIndex{}).isStatement()
-	Statement(&DropVectorIndex{}).isStatement()
+	Statement(&CreateView{}).isStatement()
+	Statement(&DropView{}).isStatement()
+	Statement(&CreateChangeStream{}).isStatement()
+	Statement(&AlterChangeStream{}).isStatement()
+	Statement(&DropChangeStream{}).isStatement()
+	Statement(&CreateRole{}).isStatement()
 	Statement(&DropRole{}).isStatement()
+	Statement(&Grant{}).isStatement()
+	Statement(&Revoke{}).isStatement()
+	Statement(&CreateSequence{}).isStatement()
+	Statement(&AlterSequence{}).isStatement()
+	Statement(&DropSequence{}).isStatement()
+	Statement(&CreateVectorIndex{}).isStatement()
+	Statement(&DropVectorIndex{}).isStatement()
 	Statement(&Insert{}).isStatement()
 	Statement(&Delete{}).isStatement()
 	Statement(&Update{}).isStatement()
-	Statement(&Grant{}).isStatement()
-	Statement(&Revoke{}).isStatement()
 }
 
 func TestQueryExpr(t *testing.T) {
@@ -130,23 +137,25 @@ func TestStringValue(t *testing.T) {
 func TestDDL(t *testing.T) {
 	DDL(&CreateDatabase{}).isDDL()
 	DDL(&CreateTable{}).isDDL()
-	DDL(&CreateIndex{}).isDDL()
-	DDL(&CreateVectorIndex{}).isDDL()
-	DDL(&CreateSequence{}).isDDL()
-	DDL(&AlterSequence{}).isDDL()
-	DDL(&DropSequence{}).isDDL()
-	DDL(&CreateView{}).isDDL()
-	DDL(&DropView{}).isDDL()
 	DDL(&AlterTable{}).isDDL()
 	DDL(&DropTable{}).isDDL()
 	DDL(&CreateIndex{}).isDDL()
 	DDL(&AlterIndex{}).isDDL()
 	DDL(&DropIndex{}).isDDL()
-	DDL(&DropVectorIndex{}).isDDL()
+	DDL(&CreateView{}).isDDL()
+	DDL(&DropView{}).isDDL()
+	DDL(&CreateChangeStream{}).isDDL()
+	DDL(&AlterChangeStream{}).isDDL()
+	DDL(&DropChangeStream{}).isDDL()
 	DDL(&CreateRole{}).isDDL()
 	DDL(&DropRole{}).isDDL()
 	DDL(&Grant{}).isDDL()
 	DDL(&Revoke{}).isDDL()
+	DDL(&CreateSequence{}).isDDL()
+	DDL(&AlterSequence{}).isDDL()
+	DDL(&DropSequence{}).isDDL()
+	DDL(&CreateVectorIndex{}).isDDL()
+	DDL(&DropVectorIndex{}).isDDL()
 }
 
 func TestConstraint(t *testing.T) {
@@ -161,7 +170,6 @@ func TestTableAlteration(t *testing.T) {
 	TableAlteration(&DropConstraint{}).isTableAlteration()
 	TableAlteration(&SetOnDelete{}).isTableAlteration()
 	TableAlteration(&AlterColumn{}).isTableAlteration()
-	TableAlteration(&AlterColumnSet{}).isTableAlteration()
 }
 
 func TestPrivilege(t *testing.T) {
diff --git a/ast/options_test.go b/ast/options_test.go
index ad952d5..d0b5792 100644
--- a/ast/options_test.go
+++ b/ast/options_test.go
@@ -2,8 +2,9 @@ package ast
 
 import (
 	"errors"
-	"github.com/google/go-cmp/cmp"
 	"testing"
+
+	"github.com/google/go-cmp/cmp"
 )
 
 func TestOptions_BoolField(t *testing.T) {
@@ -53,7 +54,7 @@ func TestOptions_BoolField(t *testing.T) {
 			},
 			name:    "bool_option",
 			want:    nil,
-			wantErr: FieldNotFound,
+			wantErr: ErrFieldNotFound,
 		},
 		{
 			desc: "invalid type",
@@ -63,7 +64,7 @@ func TestOptions_BoolField(t *testing.T) {
 				},
 			},
 			name:    "string_option",
-			wantErr: fieldTypeMismatch,
+			wantErr: errFieldTypeMismatch,
 		},
 	}
 
@@ -123,7 +124,7 @@ func TestOptions_StringField(t *testing.T) {
 			},
 			name:    "string_field",
 			want:    nil,
-			wantErr: FieldNotFound,
+			wantErr: ErrFieldNotFound,
 		},
 		{
 			desc: "invalid value",
@@ -133,7 +134,7 @@ func TestOptions_StringField(t *testing.T) {
 				},
 			},
 			name:    "bool_option",
-			wantErr: fieldTypeMismatch,
+			wantErr: errFieldTypeMismatch,
 		},
 	}
 
@@ -193,7 +194,7 @@ func TestOptions_IntegerField(t *testing.T) {
 			},
 			name:    "integer_option",
 			want:    nil,
-			wantErr: FieldNotFound,
+			wantErr: ErrFieldNotFound,
 		},
 		{
 			desc: "invalid value",
@@ -203,7 +204,7 @@ func TestOptions_IntegerField(t *testing.T) {
 				},
 			},
 			name:    "bool_option",
-			wantErr: fieldTypeMismatch,
+			wantErr: errFieldTypeMismatch,
 		},
 	}
 
diff --git a/ast/pos_test.go b/ast/pos_test.go
deleted file mode 100644
index f7f54bf..0000000
--- a/ast/pos_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package ast
-
-import (
-	"testing"
-
-	"github.com/cloudspannerecosystem/memefish/token"
-)
-
-func TestTableAlteration_Position(t *testing.T) {
-	alterColumnDefaultExpr := AlterColumn{
-		DefaultExpr: &ColumnDefaultExpr{Rparen: 100},
-	}
-	if alterColumnDefaultExpr.End() != alterColumnDefaultExpr.DefaultExpr.End() {
-		t.Fatalf("Mismatched end postion of the alter column")
-	}
-	alterColumnNull := AlterColumn{
-		Null:    101,
-		NotNull: true,
-	}
-	if alterColumnNull.End() != alterColumnNull.Null+4 {
-		t.Fatalf("Mismatched end postion of the alter column")
-	}
-	alterColumnType := AlterColumn{
-		Null: token.InvalidPos,
-		Type: &ScalarSchemaType{NamePos: 102},
-	}
-	if alterColumnType.End() != alterColumnType.Type.End() {
-		t.Fatalf("Mismatched end postion of the alter column")
-	}
-
-	alterColumnSetDefault := AlterColumnSet{
-		DefaultExpr: &ColumnDefaultExpr{Rparen: 103},
-	}
-	if alterColumnSetDefault.End() != alterColumnSetDefault.DefaultExpr.End() {
-		t.Fatalf("Mismatched end postion of the alter column set")
-	}
-	alterColumnSetOptions := AlterColumnSet{
-		Options: &Options{Rparen: 104},
-	}
-	if alterColumnSetOptions.End() != alterColumnSetOptions.Options.End() {
-		t.Fatalf("Mismatched end postion of the alter column set")
-	}
-}
diff --git a/testdata/input/ddl/alter_table_alter_column_drop_default.sql b/testdata/input/ddl/alter_table_alter_column_drop_default.sql
new file mode 100644
index 0000000..fd689f0
--- /dev/null
+++ b/testdata/input/ddl/alter_table_alter_column_drop_default.sql
@@ -0,0 +1,1 @@
+alter table foo alter column foo drop default
\ No newline at end of file
diff --git a/testdata/input/expr/conversion_functions_safe_cast.sql b/testdata/input/expr/conversion_functions_safe_cast.sql
new file mode 100644
index 0000000..a7ec0fe
--- /dev/null
+++ b/testdata/input/expr/conversion_functions_safe_cast.sql
@@ -0,0 +1,1 @@
+SAFE_CAST("apple" AS INT64)
\ No newline at end of file
diff --git a/testdata/result/ddl/alter_table_alter_column.sql.txt b/testdata/result/ddl/alter_table_alter_column.sql.txt
index dedb4be..93422dd 100644
--- a/testdata/result/ddl/alter_table_alter_column.sql.txt
+++ b/testdata/result/ddl/alter_table_alter_column.sql.txt
@@ -10,26 +10,28 @@ alter table foo alter column foo string(256) not null
   },
   TableAlteration: &ast.AlterColumn{
     Alter: 16,
-    Null:  49,
     Name:  &ast.Ident{
       NamePos: 29,
       NameEnd: 32,
       Name:    "foo",
     },
-    Type: &ast.SizedSchemaType{
-      NamePos: 33,
-      Rparen:  43,
-      Name:    "STRING",
-      Max:     false,
-      Size:    &ast.IntLiteral{
-        ValuePos: 40,
-        ValueEnd: 43,
-        Base:     10,
-        Value:    "256",
+    Alteration: &ast.AlterColumnType{
+      Type: &ast.SizedSchemaType{
+        NamePos: 33,
+        Rparen:  43,
+        Name:    "STRING",
+        Max:     false,
+        Size:    &ast.IntLiteral{
+          ValuePos: 40,
+          ValueEnd: 43,
+          Base:     10,
+          Value:    "256",
+        },
       },
+      Null:        49,
+      NotNull:     true,
+      DefaultExpr: (*ast.ColumnDefaultExpr)(nil),
     },
-    NotNull:     true,
-    DefaultExpr: (*ast.ColumnDefaultExpr)(nil),
   },
 }
 
diff --git a/testdata/result/ddl/alter_table_alter_column_drop_default.sql.txt b/testdata/result/ddl/alter_table_alter_column_drop_default.sql.txt
new file mode 100644
index 0000000..3df5dcc
--- /dev/null
+++ b/testdata/result/ddl/alter_table_alter_column_drop_default.sql.txt
@@ -0,0 +1,26 @@
+--- alter_table_alter_column_drop_default.sql
+alter table foo alter column foo drop default
+--- AST
+&ast.AlterTable{
+  Alter: 0,
+  Name:  &ast.Ident{
+    NamePos: 12,
+    NameEnd: 15,
+    Name:    "foo",
+  },
+  TableAlteration: &ast.AlterColumn{
+    Alter: 16,
+    Name:  &ast.Ident{
+      NamePos: 29,
+      NameEnd: 32,
+      Name:    "foo",
+    },
+    Alteration: &ast.AlterColumnDropDefault{
+      Drop:    33,
+      Default: 38,
+    },
+  },
+}
+
+--- SQL
+ALTER TABLE foo ALTER COLUMN foo DROP DEFAULT
diff --git a/testdata/result/ddl/alter_table_alter_column_set.sql.txt b/testdata/result/ddl/alter_table_alter_column_set.sql.txt
index 421e02d..f5b9d50 100644
--- a/testdata/result/ddl/alter_table_alter_column_set.sql.txt
+++ b/testdata/result/ddl/alter_table_alter_column_set.sql.txt
@@ -8,31 +8,33 @@ alter table foo alter column foo set options(allow_commit_timestamp = true)
     NameEnd: 15,
     Name:    "foo",
   },
-  TableAlteration: &ast.AlterColumnSet{
+  TableAlteration: &ast.AlterColumn{
     Alter: 16,
     Name:  &ast.Ident{
       NamePos: 29,
       NameEnd: 32,
       Name:    "foo",
     },
-    Options: &ast.Options{
-      Options: 37,
-      Rparen:  74,
-      Records: []*ast.OptionsDef{
-        &ast.OptionsDef{
-          Name: &ast.Ident{
-            NamePos: 45,
-            NameEnd: 67,
-            Name:    "allow_commit_timestamp",
-          },
-          Value: &ast.BoolLiteral{
-            ValuePos: 70,
-            Value:    true,
+    Alteration: &ast.AlterColumnSetOptions{
+      Set:     33,
+      Options: &ast.Options{
+        Options: 37,
+        Rparen:  74,
+        Records: []*ast.OptionsDef{
+          &ast.OptionsDef{
+            Name: &ast.Ident{
+              NamePos: 45,
+              NameEnd: 67,
+              Name:    "allow_commit_timestamp",
+            },
+            Value: &ast.BoolLiteral{
+              ValuePos: 70,
+              Value:    true,
+            },
           },
         },
       },
     },
-    DefaultExpr: (*ast.ColumnDefaultExpr)(nil),
   },
 }
 
diff --git a/testdata/result/ddl/alter_table_alter_column_set_default.sql.txt b/testdata/result/ddl/alter_table_alter_column_set_default.sql.txt
index 2c582df..af5c786 100644
--- a/testdata/result/ddl/alter_table_alter_column_set_default.sql.txt
+++ b/testdata/result/ddl/alter_table_alter_column_set_default.sql.txt
@@ -8,21 +8,23 @@ ALTER TABLE actions ALTER COLUMN output SET DEFAULT("")
     NameEnd: 19,
     Name:    "actions",
   },
-  TableAlteration: &ast.AlterColumnSet{
+  TableAlteration: &ast.AlterColumn{
     Alter: 20,
     Name:  &ast.Ident{
       NamePos: 33,
       NameEnd: 39,
       Name:    "output",
     },
-    Options:     (*ast.Options)(nil),
-    DefaultExpr: &ast.ColumnDefaultExpr{
-      Default: 44,
-      Rparen:  54,
-      Expr:    &ast.StringLiteral{
-        ValuePos: 52,
-        ValueEnd: 54,
-        Value:    "",
+    Alteration: &ast.AlterColumnSetDefault{
+      Set:         40,
+      DefaultExpr: &ast.ColumnDefaultExpr{
+        Default: 44,
+        Rparen:  54,
+        Expr:    &ast.StringLiteral{
+          ValuePos: 52,
+          ValueEnd: 54,
+          Value:    "",
+        },
       },
     },
   },
diff --git a/testdata/result/ddl/alter_table_alter_column_with_default.sql.txt b/testdata/result/ddl/alter_table_alter_column_with_default.sql.txt
index 3578bfd..b2e5c06 100644
--- a/testdata/result/ddl/alter_table_alter_column_with_default.sql.txt
+++ b/testdata/result/ddl/alter_table_alter_column_with_default.sql.txt
@@ -10,27 +10,29 @@ ALTER TABLE actions ALTER COLUMN output STRING(MAX) NOT NULL DEFAULT("")
   },
   TableAlteration: &ast.AlterColumn{
     Alter: 20,
-    Null:  56,
     Name:  &ast.Ident{
       NamePos: 33,
       NameEnd: 39,
       Name:    "output",
     },
-    Type: &ast.SizedSchemaType{
-      NamePos: 40,
-      Rparen:  50,
-      Name:    "STRING",
-      Max:     true,
-      Size:    nil,
-    },
-    NotNull:     true,
-    DefaultExpr: &ast.ColumnDefaultExpr{
-      Default: 61,
-      Rparen:  71,
-      Expr:    &ast.StringLiteral{
-        ValuePos: 69,
-        ValueEnd: 71,
-        Value:    "",
+    Alteration: &ast.AlterColumnType{
+      Type: &ast.SizedSchemaType{
+        NamePos: 40,
+        Rparen:  50,
+        Name:    "STRING",
+        Max:     true,
+        Size:    nil,
+      },
+      Null:        56,
+      NotNull:     true,
+      DefaultExpr: &ast.ColumnDefaultExpr{
+        Default: 61,
+        Rparen:  71,
+        Expr:    &ast.StringLiteral{
+          ValuePos: 69,
+          ValueEnd: 71,
+          Value:    "",
+        },
       },
     },
   },
diff --git a/testdata/result/expr/cast_as_typename.sql.txt b/testdata/result/expr/cast_as_typename.sql.txt
index ec1e5fe..3edb8be 100644
--- a/testdata/result/expr/cast_as_typename.sql.txt
+++ b/testdata/result/expr/cast_as_typename.sql.txt
@@ -4,6 +4,7 @@ CAST('order_number: "123"' AS examples.shipping.`Order`)
 &ast.CastExpr{
   Cast:   0,
   Rparen: 55,
+  Safe:   false,
   Expr:   &ast.StringLiteral{
     ValuePos: 5,
     ValueEnd: 26,
diff --git a/testdata/result/expr/conversion_functions_safe_cast.sql.txt b/testdata/result/expr/conversion_functions_safe_cast.sql.txt
new file mode 100644
index 0000000..8f9d1cb
--- /dev/null
+++ b/testdata/result/expr/conversion_functions_safe_cast.sql.txt
@@ -0,0 +1,20 @@
+--- conversion_functions_safe_cast.sql
+SAFE_CAST("apple" AS INT64)
+--- AST
+&ast.CastExpr{
+  Cast:   0,
+  Rparen: 26,
+  Safe:   true,
+  Expr:   &ast.StringLiteral{
+    ValuePos: 10,
+    ValueEnd: 17,
+    Value:    "apple",
+  },
+  Type: &ast.SimpleType{
+    NamePos: 21,
+    Name:    "INT64",
+  },
+}
+
+--- SQL
+SAFE_CAST("apple" AS INT64)
diff --git a/testdata/result/query/select_cast.sql.txt b/testdata/result/query/select_cast.sql.txt
index de64735..d5f6fb9 100644
--- a/testdata/result/query/select_cast.sql.txt
+++ b/testdata/result/query/select_cast.sql.txt
@@ -18,6 +18,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64)
         Expr: &ast.CastExpr{
           Cast:   7,
           Rparen: 22,
+          Safe:   false,
           Expr:   &ast.IntLiteral{
             ValuePos: 12,
             ValueEnd: 13,
@@ -34,6 +35,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64)
         Expr: &ast.CastExpr{
           Cast:   25,
           Rparen: 44,
+          Safe:   false,
           Expr:   &ast.FloatLiteral{
             ValuePos: 30,
             ValueEnd: 33,
@@ -49,6 +51,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64)
         Expr: &ast.CastExpr{
           Cast:   47,
           Rparen: 147,
+          Safe:   false,
           Expr:   &ast.StructLiteral{
             Struct: 0,
             Lparen: 52,
diff --git a/testdata/result/statement/alter_table_alter_column.sql.txt b/testdata/result/statement/alter_table_alter_column.sql.txt
index dedb4be..93422dd 100644
--- a/testdata/result/statement/alter_table_alter_column.sql.txt
+++ b/testdata/result/statement/alter_table_alter_column.sql.txt
@@ -10,26 +10,28 @@ alter table foo alter column foo string(256) not null
   },
   TableAlteration: &ast.AlterColumn{
     Alter: 16,
-    Null:  49,
     Name:  &ast.Ident{
       NamePos: 29,
       NameEnd: 32,
       Name:    "foo",
     },
-    Type: &ast.SizedSchemaType{
-      NamePos: 33,
-      Rparen:  43,
-      Name:    "STRING",
-      Max:     false,
-      Size:    &ast.IntLiteral{
-        ValuePos: 40,
-        ValueEnd: 43,
-        Base:     10,
-        Value:    "256",
+    Alteration: &ast.AlterColumnType{
+      Type: &ast.SizedSchemaType{
+        NamePos: 33,
+        Rparen:  43,
+        Name:    "STRING",
+        Max:     false,
+        Size:    &ast.IntLiteral{
+          ValuePos: 40,
+          ValueEnd: 43,
+          Base:     10,
+          Value:    "256",
+        },
       },
+      Null:        49,
+      NotNull:     true,
+      DefaultExpr: (*ast.ColumnDefaultExpr)(nil),
     },
-    NotNull:     true,
-    DefaultExpr: (*ast.ColumnDefaultExpr)(nil),
   },
 }
 
diff --git a/testdata/result/statement/alter_table_alter_column_drop_default.sql.txt b/testdata/result/statement/alter_table_alter_column_drop_default.sql.txt
new file mode 100644
index 0000000..3df5dcc
--- /dev/null
+++ b/testdata/result/statement/alter_table_alter_column_drop_default.sql.txt
@@ -0,0 +1,26 @@
+--- alter_table_alter_column_drop_default.sql
+alter table foo alter column foo drop default
+--- AST
+&ast.AlterTable{
+  Alter: 0,
+  Name:  &ast.Ident{
+    NamePos: 12,
+    NameEnd: 15,
+    Name:    "foo",
+  },
+  TableAlteration: &ast.AlterColumn{
+    Alter: 16,
+    Name:  &ast.Ident{
+      NamePos: 29,
+      NameEnd: 32,
+      Name:    "foo",
+    },
+    Alteration: &ast.AlterColumnDropDefault{
+      Drop:    33,
+      Default: 38,
+    },
+  },
+}
+
+--- SQL
+ALTER TABLE foo ALTER COLUMN foo DROP DEFAULT
diff --git a/testdata/result/statement/alter_table_alter_column_set.sql.txt b/testdata/result/statement/alter_table_alter_column_set.sql.txt
index 421e02d..f5b9d50 100644
--- a/testdata/result/statement/alter_table_alter_column_set.sql.txt
+++ b/testdata/result/statement/alter_table_alter_column_set.sql.txt
@@ -8,31 +8,33 @@ alter table foo alter column foo set options(allow_commit_timestamp = true)
     NameEnd: 15,
     Name:    "foo",
   },
-  TableAlteration: &ast.AlterColumnSet{
+  TableAlteration: &ast.AlterColumn{
     Alter: 16,
     Name:  &ast.Ident{
       NamePos: 29,
       NameEnd: 32,
       Name:    "foo",
     },
-    Options: &ast.Options{
-      Options: 37,
-      Rparen:  74,
-      Records: []*ast.OptionsDef{
-        &ast.OptionsDef{
-          Name: &ast.Ident{
-            NamePos: 45,
-            NameEnd: 67,
-            Name:    "allow_commit_timestamp",
-          },
-          Value: &ast.BoolLiteral{
-            ValuePos: 70,
-            Value:    true,
+    Alteration: &ast.AlterColumnSetOptions{
+      Set:     33,
+      Options: &ast.Options{
+        Options: 37,
+        Rparen:  74,
+        Records: []*ast.OptionsDef{
+          &ast.OptionsDef{
+            Name: &ast.Ident{
+              NamePos: 45,
+              NameEnd: 67,
+              Name:    "allow_commit_timestamp",
+            },
+            Value: &ast.BoolLiteral{
+              ValuePos: 70,
+              Value:    true,
+            },
           },
         },
       },
     },
-    DefaultExpr: (*ast.ColumnDefaultExpr)(nil),
   },
 }
 
diff --git a/testdata/result/statement/alter_table_alter_column_set_default.sql.txt b/testdata/result/statement/alter_table_alter_column_set_default.sql.txt
index 2c582df..af5c786 100644
--- a/testdata/result/statement/alter_table_alter_column_set_default.sql.txt
+++ b/testdata/result/statement/alter_table_alter_column_set_default.sql.txt
@@ -8,21 +8,23 @@ ALTER TABLE actions ALTER COLUMN output SET DEFAULT("")
     NameEnd: 19,
     Name:    "actions",
   },
-  TableAlteration: &ast.AlterColumnSet{
+  TableAlteration: &ast.AlterColumn{
     Alter: 20,
     Name:  &ast.Ident{
       NamePos: 33,
       NameEnd: 39,
       Name:    "output",
     },
-    Options:     (*ast.Options)(nil),
-    DefaultExpr: &ast.ColumnDefaultExpr{
-      Default: 44,
-      Rparen:  54,
-      Expr:    &ast.StringLiteral{
-        ValuePos: 52,
-        ValueEnd: 54,
-        Value:    "",
+    Alteration: &ast.AlterColumnSetDefault{
+      Set:         40,
+      DefaultExpr: &ast.ColumnDefaultExpr{
+        Default: 44,
+        Rparen:  54,
+        Expr:    &ast.StringLiteral{
+          ValuePos: 52,
+          ValueEnd: 54,
+          Value:    "",
+        },
       },
     },
   },
diff --git a/testdata/result/statement/alter_table_alter_column_with_default.sql.txt b/testdata/result/statement/alter_table_alter_column_with_default.sql.txt
index 3578bfd..b2e5c06 100644
--- a/testdata/result/statement/alter_table_alter_column_with_default.sql.txt
+++ b/testdata/result/statement/alter_table_alter_column_with_default.sql.txt
@@ -10,27 +10,29 @@ ALTER TABLE actions ALTER COLUMN output STRING(MAX) NOT NULL DEFAULT("")
   },
   TableAlteration: &ast.AlterColumn{
     Alter: 20,
-    Null:  56,
     Name:  &ast.Ident{
       NamePos: 33,
       NameEnd: 39,
       Name:    "output",
     },
-    Type: &ast.SizedSchemaType{
-      NamePos: 40,
-      Rparen:  50,
-      Name:    "STRING",
-      Max:     true,
-      Size:    nil,
-    },
-    NotNull:     true,
-    DefaultExpr: &ast.ColumnDefaultExpr{
-      Default: 61,
-      Rparen:  71,
-      Expr:    &ast.StringLiteral{
-        ValuePos: 69,
-        ValueEnd: 71,
-        Value:    "",
+    Alteration: &ast.AlterColumnType{
+      Type: &ast.SizedSchemaType{
+        NamePos: 40,
+        Rparen:  50,
+        Name:    "STRING",
+        Max:     true,
+        Size:    nil,
+      },
+      Null:        56,
+      NotNull:     true,
+      DefaultExpr: &ast.ColumnDefaultExpr{
+        Default: 61,
+        Rparen:  71,
+        Expr:    &ast.StringLiteral{
+          ValuePos: 69,
+          ValueEnd: 71,
+          Value:    "",
+        },
       },
     },
   },
diff --git a/testdata/result/statement/select_cast.sql.txt b/testdata/result/statement/select_cast.sql.txt
index de64735..d5f6fb9 100644
--- a/testdata/result/statement/select_cast.sql.txt
+++ b/testdata/result/statement/select_cast.sql.txt
@@ -18,6 +18,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64)
         Expr: &ast.CastExpr{
           Cast:   7,
           Rparen: 22,
+          Safe:   false,
           Expr:   &ast.IntLiteral{
             ValuePos: 12,
             ValueEnd: 13,
@@ -34,6 +35,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64)
         Expr: &ast.CastExpr{
           Cast:   25,
           Rparen: 44,
+          Safe:   false,
           Expr:   &ast.FloatLiteral{
             ValuePos: 30,
             ValueEnd: 33,
@@ -49,6 +51,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64)
         Expr: &ast.CastExpr{
           Cast:   47,
           Rparen: 147,
+          Safe:   false,
           Expr:   &ast.StructLiteral{
             Struct: 0,
             Lparen: 52,