spanner/spansql: support UNIQUE or NULL_FILTERED in CREATE INDEX statements

This doesn't have any impact on spannertest.

Updates #1181.

Change-Id: I01f3c9c7646c9c99e6549fcd9e0257e498ce618c
Reviewed-on: https://code-review.googlesource.com/c/gocloud/+/43395
Reviewed-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Knut Olav Løite <koloite@gmail.com>
diff --git a/spanner/spansql/parser.go b/spanner/spansql/parser.go
index fe7918d..4b7675a 100644
--- a/spanner/spansql/parser.go
+++ b/spanner/spansql/parser.go
@@ -665,10 +665,19 @@
 			{a—z|A—Z}[{a—z|A—Z|0—9|_}+]
 	*/
 
+	var unique, nullFiltered bool
+
 	if err := p.expect("CREATE"); err != nil {
 		return CreateIndex{}, err
 	}
-	// TODO: UNIQUE, NULL_FILTERED
+	if p.sniff("UNIQUE") {
+		p.expect("UNIQUE")
+		unique = true
+	}
+	if p.sniff("NULL_FILTERED") {
+		p.expect("NULL_FILTERED")
+		nullFiltered = true
+	}
 	if err := p.expect("INDEX"); err != nil {
 		return CreateIndex{}, err
 	}
@@ -683,7 +692,13 @@
 	if err != nil {
 		return CreateIndex{}, err
 	}
-	ci := CreateIndex{Name: iname, Table: tname}
+	ci := CreateIndex{
+		Name:  iname,
+		Table: tname,
+
+		Unique:       unique,
+		NullFiltered: nullFiltered,
+	}
 	ci.Columns, err = p.parseKeyPartList()
 	if err != nil {
 		return CreateIndex{}, err
diff --git a/spanner/spansql/sql.go b/spanner/spansql/sql.go
index 8a0c7a7..814a0de 100644
--- a/spanner/spansql/sql.go
+++ b/spanner/spansql/sql.go
@@ -41,7 +41,14 @@
 }
 
 func (ci CreateIndex) SQL() string {
-	str := "CREATE INDEX " + ci.Name + " ON " + ci.Table + "("
+	str := "CREATE"
+	if ci.Unique {
+		str += " UNIQUE"
+	}
+	if ci.NullFiltered {
+		str += " NULL_FILTERED"
+	}
+	str += " INDEX " + ci.Name + " ON " + ci.Table + "("
 	for i, c := range ci.Columns {
 		if i > 0 {
 			str += ", "
diff --git a/spanner/spansql/types.go b/spanner/spansql/types.go
index 91d8604..aa75fb2 100644
--- a/spanner/spansql/types.go
+++ b/spanner/spansql/types.go
@@ -44,7 +44,10 @@
 	Table   string
 	Columns []KeyPart
 
-	// TODO: UNIQUE, NULL_FILTERED, storing_clause, interleave_clause
+	Unique       bool
+	NullFiltered bool
+
+	// TODO: storing_clause, interleave_clause
 }
 
 // DropTable represents a DROP TABLE statement.