spanner/spansql: support FROM aliases in SELECT statements

This is not directly useful, since we don't support the dot operator.

Also adjust struct ordering of ComparisonOp to match all the other Op
structs.

Fixes #1973.

Change-Id: Ibffd511fc2f021138093adf8c3abe42735322308
Reviewed-on: https://code-review.googlesource.com/c/gocloud/+/56218
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 3ecfda4..6270684 100644
--- a/spanner/spansql/parser.go
+++ b/spanner/spansql/parser.go
@@ -1699,9 +1699,7 @@
 
 		// TODO: The "AS" keyword is optional.
 		if p.eat("AS") {
-			// The docs don't seem to indicate the valid lexical element for aliases,
-			// but it seems likely that identifiers are suitable.
-			alias, err := p.parseTableOrIndexOrColumnName()
+			alias, err := p.parseAlias()
 			if err != nil {
 				return nil, nil, err
 			}
@@ -1724,7 +1722,21 @@
 func (p *parser) parseSelectFrom() (SelectFrom, *parseError) {
 	// TODO: support more than a single table name.
 	tname, err := p.parseTableOrIndexOrColumnName()
-	return SelectFrom{Table: tname}, err
+	if err != nil {
+		return SelectFrom{}, err
+	}
+	sf := SelectFrom{Table: tname}
+
+	// TODO: The "AS" keyword is optional.
+	if p.eat("AS") {
+		alias, err := p.parseAlias()
+		if err != nil {
+			return SelectFrom{}, err
+		}
+		sf.Alias = alias
+	}
+
+	return sf, nil
 }
 
 func (p *parser) parseTableSample() (TableSample, *parseError) {
@@ -2226,6 +2238,12 @@
 	return be, nil
 }
 
+func (p *parser) parseAlias() (string, *parseError) {
+	// The docs don't specify what lexical token is valid for an alias,
+	// but it seems likely that it is an identifier.
+	return p.parseTableOrIndexOrColumnName()
+}
+
 func (p *parser) parseTableOrIndexOrColumnName() (string, *parseError) {
 	/*
 		table_name and column_name and index_name:
diff --git a/spanner/spansql/parser_test.go b/spanner/spansql/parser_test.go
index 1317646..d77734a 100644
--- a/spanner/spansql/parser_test.go
+++ b/spanner/spansql/parser_test.go
@@ -94,6 +94,25 @@
 				},
 			},
 		},
+		// https://github.com/googleapis/google-cloud-go/issues/1973
+		// except that "l.user_id" is replaced with "l_user_id" since we don't support
+		// the dot operator yet.
+		{`SELECT COUNT(*) AS count FROM Lists AS l WHERE l_user_id=@userID`,
+			Query{
+				Select: Select{
+					List: []Expr{
+						Func{Name: "COUNT", Args: []Expr{Star}},
+					},
+					From: []SelectFrom{{Table: "Lists", Alias: "l"}},
+					Where: ComparisonOp{
+						Op:  Eq,
+						LHS: ID("l_user_id"),
+						RHS: Param("userID"),
+					},
+					ListAliases: []string{"count"},
+				},
+			},
+		},
 	}
 	for _, test := range tests {
 		got, err := ParseQuery(test.in)
diff --git a/spanner/spansql/types.go b/spanner/spansql/types.go
index 257c793..3419a83 100644
--- a/spanner/spansql/types.go
+++ b/spanner/spansql/types.go
@@ -276,8 +276,10 @@
 
 type SelectFrom struct {
 	// This only supports a FROM clause directly from a table.
-	Table       string
-	TableSample *TableSample
+	Table string
+	Alias string // empty if not aliased
+
+	TableSample *TableSample // TODO: This isn't part of from_item; move elsewhere.
 }
 
 type Order struct {
@@ -362,8 +364,8 @@
 )
 
 type ComparisonOp struct {
-	LHS, RHS Expr
 	Op       ComparisonOperator
+	LHS, RHS Expr
 
 	// RHS2 is the third operand for BETWEEN.
 	// "<LHS> BETWEEN <RHS> AND <RHS2>".