spanner/spansql: parse STORING and INTERLEAVE IN clauses in CREATE INDEX
Change-Id: If71a6f1e318d525601d5670697b1387a48ce7b00
Reviewed-on: https://code-review.googlesource.com/c/gocloud/+/43850
Reviewed-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Knut Olav Løite <koloite@gmail.com>
diff --git a/spanner/spannertest/README.md b/spanner/spannertest/README.md
index 89b7ea2..578e9e9 100644
--- a/spanner/spannertest/README.md
+++ b/spanner/spannertest/README.md
@@ -32,7 +32,6 @@
- subselects
- set operations (UNION, INTERSECT, EXCEPT)
- SELECT star expressions
-- STORING and INTERLEAVE IN clauses for CREATE INDEX
- partition support
- conditional expressions
- table sampling (implementation)
diff --git a/spanner/spansql/parser.go b/spanner/spansql/parser.go
index af5446f..4388508 100644
--- a/spanner/spansql/parser.go
+++ b/spanner/spansql/parser.go
@@ -665,6 +665,12 @@
index_name:
{a—z|A—Z}[{a—z|A—Z|0—9|_}+]
+
+ storing_clause:
+ STORING ( column_name [, ...] )
+
+ interleave_clause:
+ INTERLEAVE IN table_name
*/
var unique, nullFiltered bool
@@ -705,6 +711,25 @@
if err != nil {
return CreateIndex{}, err
}
+
+ if p.sniff("STORING") {
+ p.expect("STORING")
+ ci.Storing, err = p.parseColumnNameList()
+ if err != nil {
+ return CreateIndex{}, err
+ }
+ }
+
+ if p.sniff(",", "INTERLEAVE", "IN") {
+ p.expect(",")
+ p.expect("INTERLEAVE")
+ p.expect("IN")
+ ci.Interleave, err = p.parseTableOrIndexOrColumnName()
+ if err != nil {
+ return CreateIndex{}, err
+ }
+ }
+
return ci, nil
}
@@ -878,6 +903,41 @@
return kp, nil
}
+func (p *parser) parseColumnNameList() ([]string, error) {
+ // TODO: This is very similar to parseKeyPartList and parseExprList. Refactor.
+
+ if err := p.expect("("); err != nil {
+ return nil, err
+ }
+ var list []string
+ for {
+ if err := p.expect(")"); err == nil {
+ break
+ }
+ p.back()
+
+ n, err := p.parseTableOrIndexOrColumnName()
+ if err != nil {
+ return nil, err
+ }
+ list = append(list, n)
+
+ // ")" or "," should be next.
+ tok := p.next()
+ if tok.err != nil {
+ return nil, err
+ }
+ if tok.value == ")" {
+ break
+ } else if tok.value == "," {
+ continue
+ } else {
+ return nil, p.errorf(`got %q, want ")" or ","`, tok.value)
+ }
+ }
+ return list, nil
+}
+
var baseTypes = map[string]TypeBase{
"BOOL": Bool,
"INT64": Int64,
diff --git a/spanner/spansql/parser_test.go b/spanner/spansql/parser_test.go
index 3e92183..ec12f44 100644
--- a/spanner/spansql/parser_test.go
+++ b/spanner/spansql/parser_test.go
@@ -157,7 +157,7 @@
) PRIMARY KEY(System, RepoPath);
CREATE UNIQUE INDEX MyFirstIndex ON FooBar (
Count DESC
- );
+ ) STORING (Count), INTERLEAVE IN SomeTable;
CREATE TABLE FooBarAux (
System STRING(MAX) NOT NULL,
RepoPath STRING(MAX) NOT NULL,
@@ -191,10 +191,12 @@
},
},
CreateIndex{
- Name: "MyFirstIndex",
- Table: "FooBar",
- Columns: []KeyPart{{Column: "Count", Desc: true}},
- Unique: true,
+ Name: "MyFirstIndex",
+ Table: "FooBar",
+ Columns: []KeyPart{{Column: "Count", Desc: true}},
+ Unique: true,
+ Storing: []string{"Count"},
+ Interleave: "SomeTable",
},
CreateTable{
Name: "FooBarAux",
diff --git a/spanner/spansql/sql.go b/spanner/spansql/sql.go
index 5a3b184..cbf28fd 100644
--- a/spanner/spansql/sql.go
+++ b/spanner/spansql/sql.go
@@ -20,6 +20,7 @@
// as the SQL dialect that this package parses.
import "strconv"
+import "strings"
func (ct CreateTable) SQL() string {
str := "CREATE TABLE " + ct.Name + " (\n"
@@ -56,6 +57,14 @@
str += c.SQL()
}
str += ")"
+ if len(ci.Storing) > 0 {
+ str += " STORING ("
+ str += strings.Join(ci.Storing, ", ")
+ str += ")"
+ }
+ if ci.Interleave != "" {
+ str += ", INTERLEAVE IN " + ci.Interleave
+ }
return str
}
diff --git a/spanner/spansql/types.go b/spanner/spansql/types.go
index 9b5cefe..542b605 100644
--- a/spanner/spansql/types.go
+++ b/spanner/spansql/types.go
@@ -47,7 +47,8 @@
Unique bool
NullFiltered bool
- // TODO: storing_clause, interleave_clause
+ Storing []string
+ Interleave string
}
// DropTable represents a DROP TABLE statement.