Skip to content

Commit

Permalink
feat(spanner/spansql): support DEFAULT keyword (#5932)
Browse files Browse the repository at this point in the history
* feat(spanner/spansql): support DEFAULT keyword

* refactor(spanner/spansql): update column def spec

* refactor(spanner/spansql): fix receiver names

Co-authored-by: rahul2393 <rahulyadavsep92@gmail.com>
  • Loading branch information
neglect-yp and rahul2393 committed Apr 27, 2022
1 parent bca8d50 commit 49c19a9
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 3 deletions.
1 change: 1 addition & 0 deletions spanner/spannertest/README.md
Expand Up @@ -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
Expand Down
44 changes: 42 additions & 2 deletions spanner/spansql/parser.go
Expand Up @@ -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()
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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
}

Expand Down
56 changes: 55 additions & 1 deletion spanner/spansql/parser_test.go
Expand Up @@ -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{
Expand Down Expand Up @@ -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."}},
Expand All @@ -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{
Expand Down
14 changes: 14 additions & 0 deletions spanner/spansql/sql.go
Expand Up @@ -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
}

Expand All @@ -170,6 +173,14 @@ func (sco SetColumnOptions) SQL() string {
return "SET " + sco.Options.SQL()
}

func (sd SetDefault) SQL() string {
return "SET DEFAULT (" + sd.Default.SQL() + ")"
}

func (dp DropDefault) SQL() string {
return "DROP DEFAULT"
}

func (co ColumnOptions) SQL() string {
str := "OPTIONS ("
if co.AllowCommitTimestamp != nil {
Expand Down Expand Up @@ -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"
}
Expand Down
44 changes: 44 additions & 0 deletions spanner/spansql/sql_test.go
Expand Up @@ -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"},
Expand All @@ -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,
},
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down
10 changes: 10 additions & 0 deletions spanner/spansql/types.go
Expand Up @@ -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 (
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 49c19a9

Please sign in to comment.