From f839b6736d0014e8ae75ced66151ae393580c3f4 Mon Sep 17 00:00:00 2001 From: neglect-yp Date: Fri, 22 Apr 2022 23:14:56 +0900 Subject: [PATCH 1/3] feat(spanner/spansql): support DEFAULT keyword --- spanner/spannertest/README.md | 1 + spanner/spansql/parser.go | 42 ++++++++++++++++++++++++- spanner/spansql/parser_test.go | 56 +++++++++++++++++++++++++++++++++- spanner/spansql/sql.go | 14 +++++++++ spanner/spansql/sql_test.go | 44 ++++++++++++++++++++++++++ spanner/spansql/types.go | 10 ++++++ 6 files changed, 165 insertions(+), 2 deletions(-) diff --git a/spanner/spannertest/README.md b/spanner/spannertest/README.md index d7504fd2af3..e737bd6810f 100644 --- a/spanner/spannertest/README.md +++ b/spanner/spannertest/README.md @@ -23,6 +23,7 @@ by ascending esotericism: - more aggregation functions - SELECT HAVING - more literal types +- DEFAULT - expressions that return null for generated columns - generated columns referencing other generated columns - checking dependencies on a generated column before deleting a column diff --git a/spanner/spansql/parser.go b/spanner/spansql/parser.go index 3f766d8740c..9146d213bf3 100644 --- a/spanner/spansql/parser.go +++ b/spanner/spansql/parser.go @@ -1547,6 +1547,16 @@ func (p *parser) parseColumnDef() (ColumnDef, *parseError) { cd.NotNull = true } + if p.eat("DEFAULT", "(") { + cd.Default, err = p.parseExpr() + if err != nil { + return ColumnDef{}, err + } + if err := p.expect(")"); err != nil { + return ColumnDef{}, err + } + } + if p.eat("AS", "(") { cd.Generated, err = p.parseExpr() if err != nil { @@ -1573,9 +1583,29 @@ func (p *parser) parseColumnDef() (ColumnDef, *parseError) { func (p *parser) parseColumnAlteration() (ColumnAlteration, *parseError) { debugf("parseColumnAlteration: %v", p) /* - { data_type } [ NOT NULL ] | SET [ options_def ] + { + data_type [ NOT NULL ] [ DEFAULT ( expression ) ] + | SET ( options_def ) + | SET DEFAULT ( expression ) + | DROP DEFAULT + } */ + if p.eat("SET", "DEFAULT", "(") { + d, err := p.parseExpr() + if err != nil { + return nil, err + } + if err := p.expect(")"); err != nil { + return nil, err + } + return SetDefault{Default: d}, nil + } + + if p.eat("DROP", "DEFAULT") { + return DropDefault{}, nil + } + if p.eat("SET") { co, err := p.parseColumnOptions() if err != nil { @@ -1594,6 +1624,16 @@ func (p *parser) parseColumnAlteration() (ColumnAlteration, *parseError) { sct.NotNull = true } + if p.eat("DEFAULT", "(") { + sct.Default, err = p.parseExpr() + if err != nil { + return nil, err + } + if err := p.expect(")"); err != nil { + return nil, err + } + } + return sct, nil } diff --git a/spanner/spansql/parser_test.go b/spanner/spansql/parser_test.go index 0c5a5644cf6..3cf64f0846f 100644 --- a/spanner/spansql/parser_test.go +++ b/spanner/spansql/parser_test.go @@ -552,6 +552,16 @@ func TestParseDDL(t *testing.T) { shard_id INT64 AS (MOD(FARM_FINGERPRINT(user_id), 19)) STORED, ) PRIMARY KEY(user_id); + -- Table has a column with a default value. + CREATE TABLE DefaultCol ( + Name STRING(MAX) NOT NULL, + Age INT64 DEFAULT (0), + ) PRIMARY KEY (Name); + + ALTER TABLE DefaultCol ALTER COLUMN Age DROP DEFAULT; + ALTER TABLE DefaultCol ALTER COLUMN Age SET DEFAULT (0); + ALTER TABLE DefaultCol ALTER COLUMN Age STRING(MAX) DEFAULT ("0"); + -- Trailing comment at end of file. `, &DDL{Filename: "filename", List: []DDLStmt{ &CreateTable{ @@ -795,6 +805,49 @@ func TestParseDDL(t *testing.T) { PrimaryKey: []KeyPart{{Column: "user_id"}}, Position: line(66), }, + + &CreateTable{ + Name: "DefaultCol", + Columns: []ColumnDef{ + {Name: "Name", Type: Type{Base: String, Len: MaxLen}, NotNull: true, Position: line(77)}, + { + Name: "Age", Type: Type{Base: Int64}, + Default: IntegerLiteral(0), + Position: line(78), + }, + }, + PrimaryKey: []KeyPart{{Column: "Name"}}, + Position: line(76), + }, + &AlterTable{ + Name: "DefaultCol", + Alteration: AlterColumn{ + Name: "Age", + Alteration: DropDefault{}, + }, + Position: line(81), + }, + &AlterTable{ + Name: "DefaultCol", + Alteration: AlterColumn{ + Name: "Age", + Alteration: SetDefault{ + Default: IntegerLiteral(0), + }, + }, + Position: line(82), + }, + &AlterTable{ + Name: "DefaultCol", + Alteration: AlterColumn{ + Name: "Age", + Alteration: SetColumnType{ + Type: Type{Base: String, Len: MaxLen}, + Default: StringLiteral("0"), + }, + }, + Position: line(83), + }, }, Comments: []*Comment{ {Marker: "#", Start: line(2), End: line(2), Text: []string{"This is a comment."}}, @@ -815,9 +868,10 @@ func TestParseDDL(t *testing.T) { {Marker: "--", Isolated: true, Start: line(43), End: line(43), Text: []string{"Table with generated column."}}, {Marker: "--", Isolated: true, Start: line(49), End: line(49), Text: []string{"Table with row deletion policy."}}, + {Marker: "--", Isolated: true, Start: line(75), End: line(75), Text: []string{"Table has a column with a default value."}}, // Comment after everything else. - {Marker: "--", Isolated: true, Start: line(75), End: line(75), Text: []string{"Trailing comment at end of file."}}, + {Marker: "--", Isolated: true, Start: line(85), End: line(85), Text: []string{"Trailing comment at end of file."}}, }}}, // No trailing comma: {`ALTER TABLE T ADD COLUMN C2 INT64`, &DDL{Filename: "filename", List: []DDLStmt{ diff --git a/spanner/spansql/sql.go b/spanner/spansql/sql.go index 6d64c35e33e..1b7c5bc8ec1 100644 --- a/spanner/spansql/sql.go +++ b/spanner/spansql/sql.go @@ -162,6 +162,9 @@ func (sct SetColumnType) SQL() string { if sct.NotNull { str += " NOT NULL" } + if sct.Default != nil { + str += " DEFAULT (" + sct.Default.SQL() + ")" + } return str } @@ -170,6 +173,14 @@ func (sco SetColumnOptions) SQL() string { return "SET " + sco.Options.SQL() } +func (sco SetDefault) SQL() string { + return "SET DEFAULT (" + sco.Default.SQL() + ")" +} + +func (sco DropDefault) SQL() string { + return "DROP DEFAULT" +} + func (co ColumnOptions) SQL() string { str := "OPTIONS (" if co.AllowCommitTimestamp != nil { @@ -254,6 +265,9 @@ func (cd ColumnDef) SQL() string { if cd.NotNull { str += " NOT NULL" } + if cd.Default != nil { + str += " DEFAULT (" + cd.Default.SQL() + ")" + } if cd.Generated != nil { str += " AS (" + cd.Generated.SQL() + ") STORED" } diff --git a/spanner/spansql/sql_test.go b/spanner/spansql/sql_test.go index 8791cfec9bc..b82cde1aab0 100644 --- a/spanner/spansql/sql_test.go +++ b/spanner/spansql/sql_test.go @@ -85,6 +85,7 @@ func TestSQL(t *testing.T) { {Name: "Cl", Type: Type{Base: Timestamp}, Options: ColumnOptions{AllowCommitTimestamp: boolAddr(false)}, Position: line(13)}, {Name: "Cm", Type: Type{Base: Int64}, Generated: Func{Name: "CHAR_LENGTH", Args: []Expr{ID("Ce")}}, Position: line(14)}, {Name: "Cn", Type: Type{Base: JSON}, Position: line(15)}, + {Name: "Co", Type: Type{Base: Int64}, Default: IntegerLiteral(1), Position: line(16)}, }, PrimaryKey: []KeyPart{ {Column: "Ca"}, @@ -107,6 +108,7 @@ func TestSQL(t *testing.T) { Cl TIMESTAMP OPTIONS (allow_commit_timestamp = null), Cm INT64 AS (CHAR_LENGTH(Ce)) STORED, Cn JSON, + Co INT64 DEFAULT (1), ) PRIMARY KEY(Ca, Cb DESC)`, reparseDDL, }, @@ -266,6 +268,22 @@ func TestSQL(t *testing.T) { "ALTER TABLE Ta ALTER COLUMN Cg STRING(MAX)", reparseDDL, }, + { + &AlterTable{ + Name: "Ta", + Alteration: AlterColumn{ + Name: "Ch", + Alteration: SetColumnType{ + Type: Type{Base: String, Len: MaxLen}, + NotNull: true, + Default: StringLiteral("1"), + }, + }, + Position: line(1), + }, + "ALTER TABLE Ta ALTER COLUMN Ch STRING(MAX) NOT NULL DEFAULT (\"1\")", + reparseDDL, + }, { &AlterTable{ Name: "Ta", @@ -282,6 +300,32 @@ func TestSQL(t *testing.T) { "ALTER TABLE Ta ALTER COLUMN Ci SET OPTIONS (allow_commit_timestamp = null)", reparseDDL, }, + { + &AlterTable{ + Name: "Ta", + Alteration: AlterColumn{ + Name: "Cj", + Alteration: SetDefault{ + Default: StringLiteral("1"), + }, + }, + Position: line(1), + }, + "ALTER TABLE Ta ALTER COLUMN Cj SET DEFAULT (\"1\")", + reparseDDL, + }, + { + &AlterTable{ + Name: "Ta", + Alteration: AlterColumn{ + Name: "Ck", + Alteration: DropDefault{}, + }, + Position: line(1), + }, + "ALTER TABLE Ta ALTER COLUMN Ck DROP DEFAULT", + reparseDDL, + }, { &AlterTable{ Name: "WithRowDeletionPolicy", diff --git a/spanner/spansql/types.go b/spanner/spansql/types.go index 4a9e8f7e431..6b125bd7817 100644 --- a/spanner/spansql/types.go +++ b/spanner/spansql/types.go @@ -235,14 +235,23 @@ type ColumnAlteration interface { func (SetColumnType) isColumnAlteration() {} func (SetColumnOptions) isColumnAlteration() {} +func (SetDefault) isColumnAlteration() {} +func (DropDefault) isColumnAlteration() {} type SetColumnType struct { Type Type NotNull bool + Default Expr } type SetColumnOptions struct{ Options ColumnOptions } +type SetDefault struct { + Default Expr +} + +type DropDefault struct{} + type OnDelete int const ( @@ -320,6 +329,7 @@ type ColumnDef struct { Type Type NotNull bool + Default Expr // set if this column has a default value Generated Expr // set of this is a generated column Options ColumnOptions From 2858e36f20565c1adff06c4e07521a18dd1533a5 Mon Sep 17 00:00:00 2001 From: neglect-yp Date: Tue, 26 Apr 2022 10:46:01 +0900 Subject: [PATCH 2/3] refactor(spanner/spansql): update column def spec --- spanner/spansql/parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spanner/spansql/parser.go b/spanner/spansql/parser.go index 9146d213bf3..295ec465eb6 100644 --- a/spanner/spansql/parser.go +++ b/spanner/spansql/parser.go @@ -1528,7 +1528,7 @@ func (p *parser) parseColumnDef() (ColumnDef, *parseError) { /* column_def: - column_name {scalar_type | array_type} [NOT NULL] [AS ( expression ) STORED] [options_def] + column_name {scalar_type | array_type} [NOT NULL] [{DEFAULT ( expression ) | AS ( expression ) STORED}] [options_def] */ name, err := p.parseTableOrIndexOrColumnName() From 2242333ff8df9ddeb0fee5889abf5f130963f1ec Mon Sep 17 00:00:00 2001 From: neglect-yp Date: Tue, 26 Apr 2022 10:47:50 +0900 Subject: [PATCH 3/3] refactor(spanner/spansql): fix receiver names --- spanner/spansql/sql.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spanner/spansql/sql.go b/spanner/spansql/sql.go index 1b7c5bc8ec1..119c44145b8 100644 --- a/spanner/spansql/sql.go +++ b/spanner/spansql/sql.go @@ -173,11 +173,11 @@ func (sco SetColumnOptions) SQL() string { return "SET " + sco.Options.SQL() } -func (sco SetDefault) SQL() string { - return "SET DEFAULT (" + sco.Default.SQL() + ")" +func (sd SetDefault) SQL() string { + return "SET DEFAULT (" + sd.Default.SQL() + ")" } -func (sco DropDefault) SQL() string { +func (dp DropDefault) SQL() string { return "DROP DEFAULT" }